Image
Image
Styled image with optional caption, plus ImageGlossary for responsive 1–3 column grids. Swappable renderer for next/image and other framework optimizers.
Demo not found:
image-defaultInstallation
npx @ravikumarsurya/mdx-ui add imageUsage
Single image with caption
<Image
src="/diagrams/state-flow.png"
alt="State flows down through the component tree"
caption="State flows down"
/>Side-by-side (auto-detects 2 children → 2 columns)
<ImageGlossary>
<Image src="/before.png" alt="Before" caption="Before refactoring" />
<Image src="/after.png" alt="After" caption="After refactoring" />
</ImageGlossary>Three columns
<ImageGlossary cols={3}>
<Image src="/step1.png" alt="Step 1" caption="Mount" />
<Image src="/step2.png" alt="Step 2" caption="Update" />
<Image src="/step3.png" alt="Step 3" caption="Unmount" />
</ImageGlossary>Block-level caption
<ImageGlossary caption="React renders in two phases">
<Image src="/render.png" alt="Render phase" caption="Render" />
<Image src="/commit.png" alt="Commit phase" caption="Commit" />
</ImageGlossary>Image optimization
By default the component renders a native <img> with loading="lazy" and decoding="async" — free performance wins that work in every framework.
For framework-level optimization (CDN, WebP/AVIF conversion, responsive srcset), wrap your layout once with ImageRendererProvider. Every <Image> in your MDX content will automatically use the renderer you provide — no changes to individual MDX files.
Next.js (next/image)
// app/layout.tsx (or pages/_app.tsx)
import NextImage from "next/image";
import { ImageRendererProvider } from "@/components/mdx/image";
export default function Layout({ children }) {
return (
<ImageRendererProvider
renderer={(props) => (
<NextImage
{...props}
// next/image requires explicit dimensions or fill
width={props.width ?? 800}
height={props.height ?? 450}
/>
)}
>
{children}
</ImageRendererProvider>
);
}Remix (@remix-run/react)
// app/root.tsx
import { ImageRendererProvider } from "~/components/mdx/image";
export default function App() {
return (
<ImageRendererProvider
renderer={(props) => (
<img {...props} loading="lazy" decoding="async" fetchpriority="auto" />
)}
>
<Outlet />
</ImageRendererProvider>
);
}Plain React / Vite (no framework)
No setup needed — the default renderer already applies loading="lazy" and decoding="async".
How cols is resolved
If cols is not set, ImageGlossary counts its children and picks automatically:
| Children | Default cols |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4+ | 2 |
Props
Image
| Prop | Type | Description |
|---|---|---|
src | string | Image source URL (required) |
alt | string | Alt text for accessibility (required) |
caption | string | Optional caption below the image |
width | number | Passed through to the renderer |
height | number | Passed through to the renderer |
className | string | Extra classes on the outer <figure> |
ImageGlossary
| Prop | Type | Default | Description |
|---|---|---|---|
cols | 1 | 2 | 3 | auto | Number of columns |
caption | string | — | Caption below the entire grid |
className | string | — | Extra classes on the wrapper |
ImageRendererProvider
| Prop | Type | Description |
|---|---|---|
renderer | (props: ImgHTMLAttributes) => ReactNode | Custom image renderer function |
children | ReactNode | Your app or layout subtree |