Skip to content

Dynamic vs Static Generation

Choose the right approach for your use case.

Overview

ApproachWhen to UseProsCons
StaticContent known at build timeFast, no runtime costCan't update without rebuild
DynamicContent changes frequentlyAlways currentSlower, uses API quota
HybridMix of bothBest of both worldsMore 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

FactorStaticDynamicHybrid
Content frequencyRarely changesOften changesMixed
Number of pages<1000UnlimitedAny
Build timeLongerShortMedium
Runtime costNonePer requestMinimal
PersonalizationNoYesPartial

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 traffic

Hybrid

Build time: +2 minutes (main pages)
Runtime: 0-500ms (cached vs new)
API calls: Minimal
Monthly cost: Low

Recommendations

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

Generate stunning Open Graph images programmatically.