Appearance
Dynamic vs Static Generation
Choose the right approach for your use case.
Overview
| Approach | When to Use | Pros | Cons |
|---|---|---|---|
| Static | Content known at build time | Fast, no runtime cost | Can't update without rebuild |
| Dynamic | Content changes frequently | Always current | Slower, uses API quota |
| Hybrid | Mix of both | Best of both worlds | More complex |
Static Generation
Generate images during build time and serve as static files.
When to Use
- Blog posts
- Documentation pages
- Product pages (that don't change often)
- Landing pages
- Marketing pages
Implementation
javascript
// build-og-images.js
const fs = require('fs');
const path = require('path');
async function generateStaticOGImages() {
const pages = [
{ slug: 'home', title: 'Welcome', subtitle: 'Build amazing things' },
{ slug: 'about', title: 'About Us', subtitle: 'Our story' },
{ slug: 'pricing', title: 'Pricing', subtitle: 'Plans for every team' }
];
for (const page of pages) {
const response = await fetch('https://ogimageapi.io/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.OG_IMAGE_API_KEY
},
body: JSON.stringify({
title: page.title,
subtitle: page.subtitle,
template: 'default',
theme: 'dark'
})
});
const buffer = await response.arrayBuffer();
fs.writeFileSync(`./public/og/${page.slug}.png`, Buffer.from(buffer));
}
}
generateStaticOGImages();Usage
html
<meta property="og:image" content="/og/about.png">Benefits
- ✅ Zero runtime latency
- ✅ No API calls on page load
- ✅ Reduced API usage
- ✅ Works even if API is down
- ✅ Can be served from CDN
Dynamic Generation
Generate images on-demand when requested.
When to Use
- User-generated content
- Personalized images
- Real-time data (prices, stats)
- Search results
- Frequently changing content
Implementation
javascript
// /api/og.js
export default async function handler(req, res) {
const { title, subtitle } = req.query;
const response = await fetch('https://ogimageapi.io/api/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.OG_IMAGE_API_KEY
},
body: JSON.stringify({
title: title || 'Default Title',
subtitle: subtitle || '',
template: 'default',
theme: 'dark'
})
});
const buffer = await response.arrayBuffer();
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 'public, max-age=86400');
res.send(Buffer.from(buffer));
}Usage
html
<meta property="og:image" content="/api/og?title=Dynamic+Title&subtitle=Generated+on+demand">Benefits
- ✅ Always up-to-date
- ✅ Handles unlimited variations
- ✅ No build-time complexity
- ✅ Personalization possible
Considerations
- ⚠️ First request may be slower
- ⚠️ Uses API quota per unique request
- ⚠️ Depends on API availability
Hybrid Approach
Combine static and dynamic generation.
Strategy 1: Static with Dynamic Fallback
javascript
// Generate static images for main content
// Use dynamic for edge cases
export async function generateMetadata({ params }) {
const staticImage = checkStaticImageExists(params.slug);
return {
openGraph: {
images: staticImage
? [`/og/${params.slug}.png`]
: [`/api/og?slug=${params.slug}`]
}
};
}Strategy 2: Incremental Static Generation
javascript
// Generate on first request, then cache
async function getOGImage(params) {
const cacheKey = getCacheKey(params);
const cachedPath = `./cache/og/${cacheKey}.png`;
if (fs.existsSync(cachedPath)) {
return fs.readFileSync(cachedPath);
}
const image = await generateOGImage(params);
fs.writeFileSync(cachedPath, image);
return image;
}Strategy 3: Time-Based Refresh
javascript
// Regenerate images older than threshold
const MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
async function getOGImage(slug) {
const imagePath = `./public/og/${slug}.png`;
if (fs.existsSync(imagePath)) {
const stats = fs.statSync(imagePath);
const age = Date.now() - stats.mtimeMs;
if (age < MAX_AGE) {
return imagePath;
}
}
// Regenerate
await generateOGImage(slug);
return imagePath;
}Decision Matrix
| Factor | Static | Dynamic | Hybrid |
|---|---|---|---|
| Content frequency | Rarely changes | Often changes | Mixed |
| Number of pages | <1000 | Unlimited | Any |
| Build time | Longer | Short | Medium |
| Runtime cost | None | Per request | Minimal |
| Personalization | No | Yes | Partial |
Performance Comparison
Static
Build time: +5 minutes (for 500 pages)
Runtime: 0ms (served from CDN)
API calls: 500 (at build)
Monthly cost: ~$0 (below free tier)Dynamic
Build time: 0
Runtime: 150-500ms (first request)
API calls: Per unique visitor
Monthly cost: Depends on trafficHybrid
Build time: +2 minutes (main pages)
Runtime: 0-500ms (cached vs new)
API calls: Minimal
Monthly cost: LowRecommendations
Choose Static If:
- Building a blog or documentation site
- Content is known at build time
- SEO is critical
- You want zero runtime dependencies
Choose Dynamic If:
- Building a SaaS with user-generated content
- Content changes frequently
- You need personalization
- Building a marketplace or directory
Choose Hybrid If:
- You have both static and dynamic content
- You want optimal performance
- You have specific pages that change often
- Budget is a consideration