Prerequisites
A Next.js App Router project. Components render through next/og, which ships with Next.js.
Installation
Pick a block from the gallery and add it with the shadcn CLI.
The block is copied into your project at components/og/<name>.tsx, together
with the components it uses.
$ pnpm dlx shadcn@latest add @ogimagecn/simple
The simple block is copied into components/og/simple.tsx, and the CLI pulls in the components it depends on automatically — there is nothing else to install.
Usage
Import the installed block and return it from a route handler as an ImageResponse:
import { ImageResponse } from "next/og";
import { Simple } from "@/components/og/simple";
export function GET(request: Request) {
const { searchParams } = new URL(request.url);
return new ImageResponse(
<Simple
brand="Acme"
description="Everything new this week."
label="Blog"
title={searchParams.get("title") ?? "Hello world"}
/>,
{ height: 630, width: 1200 }
);
}Every component documents its props on its own page; the customizer shows each one live.
Add to Metadata
Point your page's Open Graph and Twitter metadata at the route:
import type { Metadata } from "next";
export const metadata: Metadata = {
openGraph: {
images: ["/og?title=Hello%20world"],
},
twitter: {
card: "summary_large_image",
images: ["/og?title=Hello%20world"],
},
};Then run the deployed URL through Scan to see the card each platform will build from it.
Custom Fonts
Satori uses the first font you pass as the default. Load the font file and hand it to ImageResponse:
import { ImageResponse } from "next/og";
import { Simple } from "@/components/og/simple";
export async function GET() {
const font = await fetch(
new URL("./Inter-SemiBold.ttf", import.meta.url)
).then((res) => res.arrayBuffer());
return new ImageResponse(
<Simple
brand="Acme"
description="Everything new this week."
label="Blog"
title="Hello world"
/>,
{
fonts: [{ data: font, name: "Inter", style: "normal", weight: 600 }],
height: 630,
width: 1200,
}
);
}