Menu
og-imagesnextjstutorial

How to Generate Dynamic OG Images in Next.js

SnapSharp Team·March 20, 2026·7 min read

How to Generate Dynamic OG Images in Next.js

Open Graph images are the thumbnail that appears when someone shares your link on Twitter, LinkedIn, or Slack. A great OG image can significantly increase click-through rates. This tutorial shows you how to generate dynamic, per-page OG images in Next.js using the SnapSharp API.

The problem with static OG images

Most developers set a single og:image for their entire site. It works, but it's a missed opportunity. Blog posts, product pages, and user profiles all deserve their own unique social cards that reflect their content.

The traditional alternative — generating images server-side with Canvas or Puppeteer — is complex, expensive to maintain, and slow.

A better approach: API-based OG images

SnapSharp handles the rendering. You pass data, get back an image.

Setting up

Install the package (optional — you can also use plain fetch):

pnpm add @snapsharp/sdk

Or just use fetch directly — the API is simple enough.

Generating images in Next.js App Router

Create an OG image route at app/og/route.ts:

import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

export async function GET(req: NextRequest) {
  const title = req.nextUrl.searchParams.get('title') ?? 'My Blog';
  const author = req.nextUrl.searchParams.get('author') ?? 'Anonymous';

  const res = await fetch('https://api.snapsharp.dev/v1/og-image', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SNAPSHARP_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      template: 'blog-post',
      data: { title, author, date: new Date().toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }) },
      format: 'jpeg',
    }),
    next: { revalidate: 3600 },
  });

  const buffer = await res.arrayBuffer();
  return new NextResponse(buffer, {
    headers: { 'Content-Type': 'image/jpeg', 'Cache-Control': 'public, max-age=3600' },
  });
}

Adding metadata to your pages

In your blog post page (app/blog/[slug]/page.tsx):

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const post = getPost(params.slug);
  const ogUrl = `https://your-site.com/og?title=${encodeURIComponent(post.title)}&author=${encodeURIComponent(post.author)}`;

  return {
    title: post.title,
    openGraph: {
      images: [{ url: ogUrl, width: 1200, height: 630 }],
    },
    twitter: {
      card: 'summary_large_image',
      images: [ogUrl],
    },
  };
}

Result

Every blog post now gets a unique, beautifully designed social card. When shared on Twitter or LinkedIn, it shows the post title, author, and publication date — all rendered automatically.

Performance tips

  • Use next: { revalidate: 3600 } to cache API responses at the CDN level
  • Add Cache-Control: public, max-age=86400 to cache OG images aggressively
  • Use format: 'jpeg' for 30–50% smaller file sizes vs PNG

The entire setup takes about 10 minutes and works for any content type — blog posts, product pages, user profiles, and more.