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-default

Installation

npx @ravikumarsurya/mdx-ui add image

Usage

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:

ChildrenDefault cols
11
22
33
4+2

Props

Image

PropTypeDescription
srcstringImage source URL (required)
altstringAlt text for accessibility (required)
captionstringOptional caption below the image
widthnumberPassed through to the renderer
heightnumberPassed through to the renderer
classNamestringExtra classes on the outer <figure>

ImageGlossary

PropTypeDefaultDescription
cols1 | 2 | 3autoNumber of columns
captionstringCaption below the entire grid
classNamestringExtra classes on the wrapper

ImageRendererProvider

PropTypeDescription
renderer(props: ImgHTMLAttributes) => ReactNodeCustom image renderer function
childrenReactNodeYour app or layout subtree