- Next.js
- Dynamic Metadata & SEO
- Dynamic OpenGraph Image Generation with @vercel/og
Dynamic OpenGraph Image Generation with @vercel/og
Next.js supports programmatically generating social share images (og:image) using opengraph-image.tsx route conventions powered by @vercel/og and Satori. This generates SVG/PNG images dynamically on the server using JSX and CSS Flexbox subsets, eliminating manual Photoshop/Figma asset generation for millions of dynamic pages.
Learning Objectives
Create dynamic OpenGraph images using the opengraph-image.tsx file convention.
Use ImageResponse to render JSX and CSS Flexbox into high-resolution PNG images.
Load custom fonts and brand logos into dynamic social cards.
Cache generated OpenGraph images at the edge CDN for high performance.
Prerequisites & Background Knowledge
Why Interviewers Care
Standard requirement for social virality, viral marketing loops, and dynamic certificate/badge generation.
The Challenge
How does Next.js dynamically generate OpenGraph images using the opengraph-image.tsx convention? Build a dynamic social card that renders a custom article title and author avatar.
Code Architecture & Implementation
1// app/blog/[slug]/opengraph-image.tsx2import { ImageResponse } from 'next/og';34export const runtime = 'edge'; // Edge runtime for fast rendering5export const alt = 'Article Social Share Card';6export const size = { width: 1200, height: 630 };7export const contentType = 'image/png';89export default async function Image({10 params,11}: {12 params: Promise<{ slug: string }>;13}) {14 const { slug } = await params;15 const post = await fetch(`https://api.cms.com/posts/${slug}`).then(res => res.json());1617 return new ImageResponse(18 (19 <div20 style={{21 background: 'linear-gradient(to bottom right, #000000, #1a1a1a)',22 width: '100%',23 height: '100%',24 display: 'flex',25 flexDirection: 'column',26 justifyContent: 'space-between',27 padding: '80px',28 color: 'white',29 }}30 >31 <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>32 <div33 style={{34 width: '40px',35 height: '40px',36 borderRadius: '50%',37 backgroundColor: '#3b82f6',38 }}39 />40 <span style={{ fontSize: '28px', fontWeight: 'bold' }}>FAANGPrep Technical Blog</span>41 </div>4243 <h144 style={{45 fontSize: '64px',46 fontWeight: 'bold',47 lineHeight: 1.2,48 }}49 >50 {post.title}51 </h1>5253 <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: '24px', color: '#9ca3af' }}>54 <span>By {post.authorName}</span>55 <span>faangprep.com</span>56 </div>57 </div>58 ),59 {60 ...size,61 }62 );63}
Architectural Explanation
When you place opengraph-image.tsx in a route segment, Next.js automatically creates an endpoint and injects <meta property="og:image" content="..."> pointing to that image in the parent page <head>. Under the hood, @vercel/og compiles the JSX tree to SVG using Satori and converts it to PNG via WebAssembly Yoga/Resvg. Because it runs at the edge and is cached via HTTP headers, generating custom images for millions of pages adds zero recurring design cost.
Common Mistakes & Anti-Patterns
Using CSS Grid inside ImageResponse JSX, which throws layout parse errors in Satori.
Loading massive multi-megabyte TTF fonts, causing Edge memory limit crashes.
Forgetting that external image URLs rendered inside ImageResponse must be accessible to the server.
Interview Follow-Up Traps
The interviewer asks: "Is ImageResponse rendered on every social bot click?" No! The resulting PNG is cached via standard HTTP Cache-Control headers at the CDN edge.
Frequently Asked Questions
Can you use Twitter cards with the same convention?
Yes! Next.js also supports `twitter-image.tsx` with identical semantics.
Can you use custom web fonts in ImageResponse?
Yes, load the font file via fetch or fs and pass the ArrayBuffer in the fonts configuration array.
