How to Generate Dynamic OG Images with Next.js and SnapAPI
Social media preview images — those rectangular cards that appear when you share a link on Twitter, LinkedIn, or Slack — can make or break your click-through rates. Studies show that posts with rich preview images get 2-3x more engagement than plain text links.
There's a simple approach: design your OG images in HTML/CSS, then screenshot them with SnapAPI. You get pixel-perfect control, any font you want, and images that look exactly like you designed them.
What We're Building
- A Next.js API route that generates OG images on-demand
- An HTML template with dynamic content (title, author, date)
- Caching so each image is generated only once
- Proper
<meta>tags for Twitter, Facebook, and LinkedIn
Step 1: Set Up Your SnapAPI API Key
After signing up at snapapi.app, grab your API key. Add it to your .env.local:
SNAPAPI_KEY=your_api_key_here
Step 2: Create the OG Image HTML Template
Design your OG image with standard HTML and CSS. The beauty of this approach is you use tools you already know:
// lib/og-template.ts
export function generateOGTemplate({ title, author, date, category }) {
return `
<!DOCTYPE html>
<html>
<head>
<style>
body {
width: 1200px; height: 630px;
display: flex; font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #0f172a, #1e293b);
color: white;
}
.title { font-size: 56px; font-weight: 800; }
</style>
</head>
<body>
<div class="container">
<span class="category">${category}</span>
<h1 class="title">${title}</h1>
<p>${author} · ${date}</p>
</div>
</body>
</html>`;
}
Step 3: Build the API Route
Create an API route that takes post data, renders the HTML, and calls SnapAPI:
// app/api/og/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const title = searchParams.get('title') || 'Untitled';
const html = generateOGTemplate({ title, ... });
const response = await fetch('https://snapapi.app/v1/screenshot', {
method: 'POST',
headers: {
'X-API-Key': process.env.SNAPAPI_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
html: html,
width: 1200, height: 630,
format: 'png',
}),
});
const imageBuffer = Buffer.from(await response.arrayBuffer());
return new NextResponse(imageBuffer, {
headers: {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=86400',
},
});
}
Step 4: Add Meta Tags
In your blog post layout, reference the OG image API route:
export async function generateMetadata({ params }): Promise<Metadata> {
const post = await getPostBySlug(params.slug);
const ogUrl = `/api/og?title=${encodeURIComponent(post.title)}`;
return {
openGraph: {
images: [{ url: ogUrl, width: 1200, height: 630 }],
},
twitter: { card: 'summary_large_image', images: [ogUrl] },
};
}
Why SnapAPI Over @vercel/og?
If your OG images are simple text on a gradient, @vercel/og works fine. But for anything complex — custom layouts, brand assets, rich typography — SnapAPI gives you full HTML/CSS freedom. Any Google Font, any CSS property, any level of complexity.
Cost Breakdown
With SnapAPI's Free tier (100 screenshots/month), you can generate OG images for 100 blog posts. The Starter plan at $19/month covers 1,000 images — more than enough for most blogs. Compare that to running your own Puppeteer instance ($20-40/month) just for OG generation.
Ready to get started?
Sign up for SnapAPI and generate your first OG image in under 5 minutes.
View Plans →