NEXT.JS APP ROUTER · DYNAMIC OG IMAGES

Give every Next.js page a Chromium-rendered social card.

Keep the API key on the server, expose a local image route to crawlers, and generate page-specific Open Graph metadata from the data your route already loads. This pattern works with the App Router without putting a secret in HTML.

1200×630OG DEFAULT
Real CSSCHROMIUM
Server-onlyAPI KEY
Free hitsCACHED REPEATS

1. Create a server-only image route

Create app/api/og/route.ts. The route accepts public page data, calls ogshot with the secret key in a request header, and streams the image response back to the social crawler. Validate the public parameters against your own content when possible; the minimal example below caps their lengths.

import { NextRequest } from "next/server"; export const runtime = "nodejs"; export async function GET(request: NextRequest) { const title = (request.nextUrl.searchParams.get("title") || "Untitled").slice(0, 90); const site = (request.nextUrl.searchParams.get("site") || "example.com").slice(0, 60); const query = new URLSearchParams({ title, site, author: "Your team" }); const image = await fetch(`https://ogshot.dev/v1/template/article?${query}`, { headers: { "x-api-key": process.env.OGSHOT_API_KEY! }, }); if (!image.ok) return new Response("OG image failed", { status: 502 }); return new Response(image.body, { headers: { "content-type": image.headers.get("content-type") || "image/png", "cache-control": "public, max-age=86400, stale-while-revalidate=604800", }, }); }

Add OGSHOT_API_KEY only to the server environment. Do not prefix it with NEXT_PUBLIC_. The returned route is public, but it contains no reusable credential.

2. Generate metadata from the page

In a dynamic route such as app/blog/[slug]/page.tsx, use the same post record for the page title and the image URL. Absolute image URLs are safest across crawlers, so configure metadataBase once in the root layout or construct the full site origin explicitly.

import type { Metadata } from "next"; export async function generateMetadata({ params }): Promise<Metadata> { const { slug } = await params; const post = await getPost(slug); const query = new URLSearchParams({ title: post.title, site: "example.com" }); const image = `https://example.com/api/og?${query}`; return { title: post.title, description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: [{ url: image, width: 1200, height: 630 }] }, twitter: { card: "summary_large_image", images: [image] }, }; }

3. Cache at both layers

ogshot caches identical render requests and does not meter cache hits. Your Next.js route can add its own CDN cache header so most social crawlers never reach the rendering service after the first successful request. Change the query data—or add a version parameter—when a post title or design changes.

A viral URL should not cause a viral Chromium bill. The first unique image renders; identical repeats can be served by the route cache and ogshot's render cache.

Alternative: return a signed image URL

If you do not want to proxy image bytes through Next.js, generate an HMAC-signed ogshot URL on the server and place the finished URL in metadata. The dashboard provides a generator and the API docs provide the canonical signing algorithm. Only the final signature and public signing id appear in HTML; the signing secret remains server-side.

Verify what platforms will receive

Deploy one page, paste it into the free OG image checker, and verify the final URL, dimensions, content type, and platform crops. Social networks cache metadata independently, so validate before sharing the production link widely. For custom layouts, replace the template request with POST /v1/render from a server route.

Related guides: Astro OG images, ogshot as a Satori alternative, and the full-page screenshot API.

NEXT.JS · FIRST CARD

Ship the route with a real render.

Start with 100 free renders each month; cached repeats do not count.