Dynamic OG image
/api/og generates an SVG per request, keyed by query string. Linking it from <meta property="og:image"> lets every page emit its own preview without committing 100 PNG variants. SVG is the smallest path; production apps that need raster output run SVG → resvg-js downstream.
Tip
Social crawlers (Facebook, Twitter/X, LinkedIn) don't follow redirects on og:image — the URL must respond directly with 200. SVG is rejected by most crawlers (they expect image/png or image/jpeg): use bext's built-in rasteriser at /__bext/api/og to serve a ready-made PNG.
Live preview
/api/og?title=Hello%2C%20OG&subtitle=rendered%20server-side&accent=d97706
// /api/og — emits a 1200x630 SVG you can use as <meta property="og:image">
export async function GET(req: Request) {
const url = new URL(req.url);
const title = url.searchParams.get("title") ?? "bext demos";
const svg = '<svg ...><text>' + escapeXml(title) + '</text></svg>';
return new Response(svg, {
status: 200,
headers: {
"content-type": "image/svg+xml; charset=utf-8",
"cache-control": "public, max-age=3600", // query-string keys the cache
},
});
}
// Per-page metadata referencing the dynamic image.
export function generateMetadata({ searchParams }) {
const title = searchParams.title ?? "bext";
return {
openGraph: {
images: ["/api/og?title=" + encodeURIComponent(title)],
},
};
}