Dynamic OG Image Generation: From SSG to Edge Functions
May 22, 2026 · 9 min read
Hard-coding OG images stops working the moment you have more than a handful of pages. Personalized previews, per-post designs, daily-changing landing pages — they all need dynamic generation. The question isn't whether to generate dynamically; it's where in the request pipeline to do it.
Three places to generate
1. Build time (SSG). Generate one PNG per page during your CI build. Pages that survive a deploy unchanged keep their OG image. Tools: next-og, satori, Playwright screenshots in CI.
2. Server-rendered on demand. Page hit triggers PNG generation, response is cached. Tools: Next.js @vercel/og, custom canvas renderer, Puppeteer in a serverless function.
3. Edge functions. Cloudflare Worker or Vercel Edge Function renders within ~50ms anywhere in the world. Best for personalization (user avatar in the image, daily quote, signed-in state).
When to use each
| Approach | Best for | Latency | Cost model |
|---|---|---|---|
| SSG | Blog posts, fixed marketing | ~0ms (CDN) | Build time only |
| Serverless | Long-tail dynamic pages | 100–500ms first hit | Per invocation |
| Edge | Personalized, low-latency | 30–80ms global | Per request |
A minimal Next.js example
// app/og/[slug]/route.tsx
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export async function GET(req: Request, { params }: { params: { slug: string } }) {
return new ImageResponse(
<div style={{ width: 1200, height: 630, background: '#0f172a', color: 'white' }}>
<h1 style={{ fontSize: 64 }}>{params.slug}</h1>
</div>,
{ width: 1200, height: 630 }
)
}FAQ
Should I generate OG images at build time or on demand?
Build time (SSG) for content that rarely changes — blog posts, marketing pages. Edge functions for personalized or frequently changing content — user profiles, dynamic listings. The trade-off: SSG is faster to serve but rebuild-bound; edge is always fresh but pays per-request compute.
What's the difference between Next.js @vercel/og and Cloudflare Workers approach?
@vercel/og uses Satori under the hood to render JSX to SVG to PNG at the edge. Cloudflare Workers can do the same via the satori-html package, or you can use the Workers AI text-to-image binding. Both run sub-200ms at the edge.
How do I cache dynamic OG images?
Set Cache-Control: public, max-age=86400, s-maxage=31536000, stale-while-revalidate=604800 on the response. CDN holds it; browser refreshes daily; stale fallback covers any rebuilds. For Cloudflare Workers, also set the Cache API entry directly in the worker.
Skip the setup — AutoOG handles it in one API call
A REST endpoint that takes any URL or text and returns a CDN-cached OG image. No infrastructure to maintain.
Get API key →