Generate OG Images Dynamically for Your Blog Posts
How I integrated the @vercel/og package into my blog to automatically build a unique OG image for every post.
Custom OG images make social link sharing more engaging. If you're using Next.js, setting up dynamic OG images should be a quick and familiar process. It takes advantage of api routes and Vercel Edge Functions.
Here's the high-level setup:
- Install the
@vercel/ogpackage - Create a component under the api route
/api/og.tsx - Return an
ImageResponsefrom that route - Use this new endpoint (
/api/og) for your blog'sog:imagemeta tags
For the full setup details go here: Full Guide
The rest of this guide goes over how I used this setup to generate dynamic images with titles, metadata, and images from blog posts.
Dynamic Content in Your Images
Unique Post data
-
The dynamic data highlighted below will change for each blog post, while everything else will remain static:

-
Blog posts are created as markdown files, and each file has frontmatter data that looks like this:
--- title: 'Build a Mailchimp Subscribe Form in Next.js' description: 'Create and embed a simple Mailchimp subscription form on your Next.js site to build your audience list.' author: 'Noah Matsell' coverImgUrl: '/blog/cover/mailchimp.png' publishDate: '2023-01-04' date: '2022-12-28' tags: - nextjs - mailchimp categories: - tutorials ---
og.tsx API Route Component
- Start with a basic component setup seen here
- Inside the component, get your URL parameter data and render them in a component passed to a new ImageResponse:
...
try {
const { searchParams } = new URL(req.url);
const postTitle = searchParams.get("title");
const publishDate = searchParams.get("publishDate");
const readTime = searchParams.get("readTime");
const coverImgUrl = searchParams.get("coverImgUrl");
if (!postTitle || !publishDate || !readTime || !coverImgUrl) {
throw new Error();
}
// Build component response below
return new ImageResponse(
(
<div>
<p>{postTitle}</p>
<p>{publishDate}</p>
<p>{readTime}</p>
<img src={coverImgUrl} />
</div>
),
{
width: 1200,
height: 630,
fonts: [],
}
)
...
- The above throws an error if any of the query parameters aren't defined. In these cases, we catch the error and return a generic fallback ImageResponse instead:
...
} catch (e) {
// Build fallback component response below
return new ImageResponse(
(<div><h1>Fallback Content</h1></div>),
{
width: 1200,
height: 630,
fonts: [],
}
)
...
See my full, non-simplified og.tsx here.
Your dynamic image is now live! Take a look at either localhost:3000/api/og or in production at yourdomain.com/api/og. Add query parameters to these URLs to test out the dynamic values passed in.
Update Your Meta Tags
- The meta tags in my app are contained in an SEO component, which returns a
Headcomponent from thenext/headpackage. - Here the
buildOgImageUrlutility builds the OG image URL with a hostname (based on environment) and url parameters.
...
const buildOgImageUrl = ({
title = "",
publishDate = "",
readTime = "",
coverImgUrl = "",
}: {
title?: string;
publishDate?: string;
readTime?: string;
coverImgUrl?: string;
}) => {
const hostname =
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: "https://www.noahmatsell.ca";
const params = `title=${encodeURIComponent(
title
)}&publishDate=${encodeURIComponent(
publishDate
)}&readTime=${encodeURIComponent(
readTime
)}&coverImgUrl=${encodeURIComponent(
coverImgUrl
)}`;
return `${hostname}/api/og?${params}`;
};
...
- Finally, inside the SEO component build the full URL and pass it to the
og:imagemeta tag
...
const ogImgUrl = buildOgImageUrl({
title,
publishDate,
readTime,
coverImgUrl,
});
return (
<Head>
<title>{`${title} // A Dev's Blog`}</title>
<meta name="author" content={author} />
<meta name="description" content={description} />
{/* update og:image with dynamic URL */}
<meta name="og:image" content={ogImgUrl} />
<meta property="og:image:type" content={ogImageType} />
<meta property="og:image:width" content={ogImageWidth} />
<meta property="og:image:height" content={ogImageHeight} />
</Head>
);
...
See my full SEO.tsx file here.
Gotchas
If you're having issues or seeing errors, check that:
- Node 16+ is installed locally
- Next version > v12.2.3 is installed
- The correct runtime config is set in the
og.tsxfile - Valid and supported CSS is used in
og.tsx.
Wrapping Up
That's all you need to dynamically generate OG images for your site! Once set up, you can rest assured that your links will look good when shared socially.



