The imageimageresponse & route handlers

One file can draw every social card you’ll ever need.

Every page you have published, and every page you have not written yet. ImageResponse turns JSX, the same markup your components are made of, into a PNG at the moment a request asks for it. It runs in a Route Handler like any other: query in, image out.

Pick a page and the server draws its card. The same route drew the link preview for this page. Paste this page’s URL into Slack and compare.

working

GET /api/og?title=The%20unofficial%20live%20playground%20for%20Next.js%20%26%20Vercel

rendering on the server…
og.png · 1200 × 630open the file ↗

one Route Handler · JSX in, PNG out · drawn per request, flexbox only

app/api/og/route.tsx
// app/api/og/route.tsx — the Route Handler that draws the card
import { ImageResponse } from "next/og";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  // one of the site's own page titles, or the site line
  const title = resolveTitle(searchParams.get("title"));
  const fonts = await loadFonts(); // real .ttf files, read once

  return new ImageResponse(
    <div style={{ display: "flex", backgroundColor: "#071e26" }}>
      {title}
    </div>,
    {
      width: 1200,
      height: 630,
      fonts: [{ name: "JetBrains Mono", data: fonts.bold, weight: 700 }],
    }
  );
}