Next.js Curriculum
MediumMetadata & SEO100% Free30 min read

Dynamic OpenGraph Image Generation with @vercel/og

The 30-Second Interview Answer

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
OpenGraph protocol (og:image)JSX syntaxCSS Flexbox layout

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.tsx
2import { ImageResponse } from 'next/og';
3
4export const runtime = 'edge'; // Edge runtime for fast rendering
5export const alt = 'Article Social Share Card';
6export const size = { width: 1200, height: 630 };
7export const contentType = 'image/png';
8
9export 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());
16
17 return new ImageResponse(
18 (
19 <div
20 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 <div
33 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>
42
43 <h1
44 style={{
45 fontSize: '64px',
46 fontWeight: 'bold',
47 lineHeight: 1.2,
48 }}
49 >
50 {post.title}
51 </h1>
52
53 <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

What is the default recommended size for OpenGraph images?

1200px width by 630px height (aspect ratio ~1.91:1).

Yes! Next.js also supports `twitter-image.tsx` with identical semantics.

Yes, load the font file via fetch or fs and pass the ArrayBuffer in the fonts configuration array.