How to Add Dynamic OG Images to Your Next.js App in 5 Minutes
How to Add Dynamic OG Images to Your Next.js App in 5 Minutes
Dynamic OG images ensure every page on your site has a unique, branded social preview. Instead of designing hundreds of images manually, you generate them on-the-fly from page data.
This tutorial shows you how to integrate ogimg.xyz with Next.js -- both the App Router (Next.js 13+) and the Pages Router. By the end, every page on your site will have a custom OG image generated automatically from its title and metadata.
Step 1: Understand the Meta Tag Basics
Social platforms read OG images from HTML meta tags in your page's :
``html
`
The og:image tag points to the image URL. The width and height tags help platforms render the preview without downloading the full image first. Twitter also recognizes these tags, but you can add Twitter-specific tags for more control:
`html
`
The summary_large_image card type tells Twitter to display the full-width image preview rather than a small thumbnail.
Step 2: Construct the ogimg.xyz API URL
The ogimg.xyz API generates images from URL parameters. The base endpoint is:
`
https://ogimg.xyz/api/og
`
You pass your content as query parameters:
`
https://ogimg.xyz/api/og?title=My+Blog+Post&description=A+deep+dive+into+React+patterns&template=default
`
Available parameters:
- title
-- The main heading text (required) - description
-- Secondary text below the title (optional) - template
-- The design template to use (optional, defaults todefault) - theme
-- Light or dark mode (optional)
Important: URL-encode your parameter values. Spaces become + or %20, special characters need proper encoding. In Next.js, use encodeURIComponent() for this.
Step 3: Add OG Images in Next.js App Router
The App Router (Next.js 13+) uses the metadata export or generateMetadata function to define meta tags.
Static Metadata
For pages with fixed content, export a metadata object:
`typescript
// app/about/page.tsx
export const metadata = {
title: 'About Us',
description: 'Learn about our team and mission.',
openGraph: {
title: 'About Us',
description: 'Learn about our team and mission.',
images: [
{
url: 'https://ogimg.xyz/api/og?title=About+Us&template=default',
width: 1200,
height: 630,
type: 'image/png',
},
],
},
twitter: {
card: 'summary_large_image',
images: ['https://ogimg.xyz/api/og?title=About+Us&template=default'],
},
};
`
Dynamic Metadata
For pages where the content varies (blog posts, product pages), use generateMetadata:
`typescript
// app/blog/[slug]/page.tsx
import { getPost } from '@/lib/posts';
export async function generateMetadata({ params }) { const post = await getPost(params.slug);
const ogImageUrl = https://ogimg.xyz/api/og?${new URLSearchParams({
title: post.title,
description: post.excerpt,
template: 'blog',
}).toString()};
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [
{
url: ogImageUrl,
width: 1200,
height: 630,
type: 'image/png',
},
],
},
twitter: {
card: 'summary_large_image',
images: [ogImageUrl],
},
};
}
`
Using URLSearchParams handles encoding automatically -- no need to manually replace spaces or escape characters.
Helper Function (Recommended)
If you use OG images across many pages, create a reusable helper:
`typescript
// lib/og-image.ts
export function getOgImageUrl(params: {
title: string;
description?: string;
template?: string;
}) {
const searchParams = new URLSearchParams({
title: params.title,
...(params.description && { description: params.description }),
template: params.template || 'default',
});
return https://ogimg.xyz/api/og?${searchParams.toString()};
}
`
Then use it in any page:
`typescript
import { getOgImageUrl } from '@/lib/og-image';
const ogImage = getOgImageUrl({
title: post.title,
description: post.excerpt,
template: 'blog',
});
`
Step 4: Add OG Images in Next.js Pages Router
If you are using the Pages Router, add OG meta tags via the next/head component:
`tsx
// pages/blog/[slug].tsx
import Head from 'next/head';
export default function BlogPost({ post }) {
const ogImageUrl = https://ogimg.xyz/api/og?${new URLSearchParams({
title: post.title,
description: post.excerpt,
template: 'blog',
}).toString()};
return ( <>
For a shared layout pattern, you can create a reusable
SEO component:
`tsx
// components/seo.tsx
import Head from 'next/head';
import { getOgImageUrl } from '@/lib/og-image';
interface SEOProps {
title: string;
description: string;
template?: string;
}
export function SEO({ title, description, template }: SEOProps) {
const ogImage = getOgImageUrl({ title, description, template });
return (
{title}
);
}
`
Then every page becomes a one-liner:
`tsx
`
Step 5: Test Your OG Images
Before pushing to production, verify your OG images render correctly using these platform debuggers:
- Facebook Sharing Debugger: developers.facebook.com/tools/debug -- Paste your URL, see the preview, and clear Facebook's cache if needed.
- Twitter Card Validator: cards-dev.twitter.com/validator -- Preview your Twitter card rendering.
- LinkedIn Post Inspector: linkedin.com/post-inspector -- Check how LinkedIn renders your link preview.
Common issues and fixes:
- Image not showing: Ensure the
og:image URL is absolute (starts with https://), not a relative path.
Old image cached: Social platforms aggressively cache OG images. Use the debugger tools to force a re-fetch, or append a cache-busting parameter like &v=2 to the image URL.
Image cropped incorrectly: Stick to 1200x630 dimensions. Keep important text centered with padding -- some platforms crop edges slightly.
Slow loading preview: If the preview takes too long to render, the platform may time out. ogimg.xyz caches images at the edge, but the first request for a unique parameter set takes slightly longer.
Quick Checklist
Before you ship, verify:
- Every page has an
og:image meta tag with an absolute URL
The og:image:width and og:image:height tags are set to 1200 and 630
The twitter:card tag is set to summary_large_image`
Related tools from CorbanWare