Skip to content

Next.js Integration

Generate dynamic OG images for your Next.js application.

Quick Start

1. Install Dependencies

bash
npm install
# No additional packages needed - uses native fetch

2. Set Environment Variable

bash
# .env.local
OG_IMAGE_API_KEY=og_your_api_key_here

3. Create API Route

javascript
// app/api/og/route.js (App Router)
import { NextResponse } from 'next/server';

export async function GET(request) {
  const { searchParams } = new URL(request.url);
  const title = searchParams.get('title') || 'Default Title';
  const subtitle = searchParams.get('subtitle') || '';
  
  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,
      subtitle,
      template: 'blog',
      theme: 'dark'
    })
  });
  
  const imageBuffer = await response.arrayBuffer();
  
  return new NextResponse(imageBuffer, {
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control': 'public, max-age=86400, s-maxage=86400'
    }
  });
}

4. Use in Metadata

javascript
// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
  const post = await getPost(params.slug);
  
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      images: [
        {
          url: `/api/og?title=${encodeURIComponent(post.title)}&subtitle=${encodeURIComponent(post.excerpt)}`,
          width: 1200,
          height: 630
        }
      ]
    },
    twitter: {
      card: 'summary_large_image',
      title: post.title,
      description: post.excerpt,
      images: [`/api/og?title=${encodeURIComponent(post.title)}`]
    }
  };
}

For best performance, generate images at build time:

javascript
// scripts/generate-og-images.js
const fs = require('fs');
const path = require('path');

async function generateOGImages() {
  const posts = await getAllPosts();
  
  for (const post of posts) {
    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: post.title,
        subtitle: post.excerpt,
        author_name: post.author,
        template: 'blog',
        theme: 'dark'
      })
    });
    
    const buffer = await response.arrayBuffer();
    const outputPath = path.join('./public/og', `${post.slug}.png`);
    fs.writeFileSync(outputPath, Buffer.from(buffer));
    
    console.log(`Generated: ${outputPath}`);
  }
}

generateOGImages();

Add to package.json:

json
{
  "scripts": {
    "generate-og": "node scripts/generate-og-images.js",
    "build": "npm run generate-og && next build"
  }
}

App Router (Next.js 13+)

Dynamic Route Handler

javascript
// app/api/og/[...slug]/route.js
import { NextResponse } from 'next/server';

export async function GET(request, { params }) {
  const slug = params.slug?.join('/') || '';
  const post = await getPostBySlug(slug);
  
  if (!post) {
    return NextResponse.json({ error: 'Not found' }, { status: 404 });
  }
  
  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: post.title,
      subtitle: post.excerpt,
      author_name: post.author.name,
      author_avatar_url: post.author.avatar,
      template: 'blog',
      theme: 'dark'
    })
  });
  
  return new NextResponse(await response.arrayBuffer(), {
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control': 'public, max-age=86400'
    }
  });
}

With generateStaticParams

javascript
// app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await getAllPosts();
  return posts.map((post) => ({ slug: post.slug }));
}

export async function generateMetadata({ params }) {
  const post = await getPost(params.slug);
  
  return {
    openGraph: {
      images: [`/og/${params.slug}.png`]
    }
  };
}

Pages Router (Next.js 12)

API Route

javascript
// pages/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 || 'My Site',
      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));
}

In getStaticProps

javascript
// pages/blog/[slug].js
import Head from 'next/head';

export async function getStaticProps({ params }) {
  const post = await getPost(params.slug);
  
  // Generate OG image at build time
  const ogResponse = 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: post.title,
      subtitle: post.excerpt,
      template: 'blog',
      theme: 'dark'
    })
  });
  
  const fs = require('fs');
  const buffer = await ogResponse.arrayBuffer();
  fs.writeFileSync(`./public/og/${params.slug}.png`, Buffer.from(buffer));
  
  return { props: { post } };
}

export default function BlogPost({ post }) {
  return (
    <>
      <Head>
        <meta property="og:image" content={`/og/${post.slug}.png`} />
        <meta name="twitter:image" content={`/og/${post.slug}.png`} />
      </Head>
      {/* Page content */}
    </>
  );
}

Caching Strategies

javascript
// app/api/og/route.js
export const runtime = 'edge';
export const revalidate = 86400; // 24 hours

export async function GET(request) {
  // ... generate image
  
  return new NextResponse(imageBuffer, {
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control': 'public, s-maxage=86400, stale-while-revalidate=604800'
    }
  });
}

Incremental Static Regeneration

javascript
// app/blog/[slug]/opengraph-image.js
export const revalidate = 86400;

export default async function Image({ params }) {
  // Generate image...
}

Error Handling

javascript
export async function GET(request) {
  try {
    const response = await fetch('https://ogimageapi.io/api/generate', {
      // ...
    });
    
    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }
    
    return new NextResponse(await response.arrayBuffer(), {
      headers: { 'Content-Type': 'image/png' }
    });
  } catch (error) {
    console.error('OG image generation failed:', error);
    
    // Return fallback image
    const fallbackImage = fs.readFileSync('./public/og-fallback.png');
    return new NextResponse(fallbackImage, {
      headers: { 'Content-Type': 'image/png' }
    });
  }
}

Best Practices

  1. Generate at build time for static content
  2. Use edge caching for dynamic content
  3. Set appropriate cache headers
  4. Have fallback images for errors
  5. Validate input to prevent errors

Generate stunning Open Graph images programmatically.