How to Generate OG Images Automatically
You have 200 blog posts and zero OG images. Or worse, you have 200 OG images that all look slightly different because three people made them over two years. Here is how to fix that.
The Problem Is Not Making One Image
I can make an OG image in Figma in about four minutes. That is not the problem. The problem is making 200 of them. The problem is making sure they all match your current brand. The problem is remembering to make one every single time you publish something new.
I forgot to add an OG image to a blog post once and did not notice for three months. It got shared on Twitter with that ugly default gray box. The post had decent content. Nobody clicked it.
So the question is not really "how do I make an OG image." It is "how do I generate OG images automatically so I never have to think about this again."
What Actually Works in 2026
I have tried most approaches to automatic OG image generation over the past few years. Some are fine. Some are terrible. Here is where things stand.
Build-Time Generation
If your site is statically generated, you can create OG images at build time. This is what a lot of documentation sites do. You run a script during your build step that loops through your pages and generates a PNG for each one.
// Build script approach
import { generateOgImage } from "./og-generator";
for (const post of allPosts) {
const image = await generateOgImage({
title: post.title,
author: post.author,
});
await fs.writeFile(`./public/og/${post.slug}.png`, image);
}This works. But your build times grow linearly with your content. 50 posts? Fine, adds maybe 30 seconds. 500 posts? Now your builds take an extra five minutes and your CI bill goes up. You also regenerate every image on every build, even if nothing changed. You can add caching to fix that, but now you are maintaining OG image caching infrastructure, which is not what you sat down to do today.
On-Demand with Headless Browsers
Puppeteer and Playwright can screenshot an HTML page and save it as a PNG. You design your template in HTML, pass in the title and description as URL parameters, and take a screenshot.
I used this approach for about eight months on a project. It works until it does not. Chromium is a 130MB binary. Serverless functions have size limits. Cold starts are brutal. And every few weeks, something breaks because a Chromium update changes rendering behavior or a memory leak crashes your function.
If you want to generate OG images automatically at scale, headless browsers are the wrong tool. They were built for browser testing, not image generation.
Edge Rendering with Satori
Satori takes JSX and converts it to SVG, which then gets rasterized to PNG. It runs on the edge. No Chromium needed. Generation time is 50-150ms instead of 2-5 seconds.
The catch is that Satori supports a subset of CSS. Flexbox works. Grid does not. Some font features are missing. If your design is simple, this is a good option. If you need complex layouts or specific typography, you will spend a lot of time fighting the renderer.
API-Based Generation
This is what I actually use now. You send parameters to an API endpoint and get back a PNG. The API handles rendering, caching, and CDN distribution. You do not manage any infrastructure.
// Generate OG images automatically with an API call
const ogImageUrl = `https://ogpix-pi.vercel.app/api/og?${new URLSearchParams({
title: post.title,
description: post.excerpt,
theme: "dark",
key: process.env.OGPIX_KEY!,
})}`;
// Use it in your meta tags
<meta property="og:image" content={ogImageUrl} />The trade-off is that you depend on an external service. If the API goes down, your images go down. In practice, this has not been an issue for me because most APIs cache aggressively at the edge, and social media platforms cache the images on their end too. But it is worth knowing.
How I Set This Up with OGPix
I will walk through the exact setup I use. It takes about five minutes.
First, go to the playground and pick a template that is close to what you want. Adjust colors, fonts, and layout until it looks right. The playground shows you a live preview, so you can see exactly what the generated image will look like before you write any code.
Then grab your API key from the dashboard. The free tier gives you 1,000 images per month. That is plenty unless you are running a site with a very large content catalog.
Now wire it into your site. Here is what it looks like in a Next.js app:
// lib/og.ts
export function getOgImageUrl(title: string, description?: string) {
const params = new URLSearchParams({
title,
theme: "dark",
key: process.env.OGPIX_KEY!,
});
if (description) params.set("description", description);
return `https://ogpix-pi.vercel.app/api/og?${params}`;
}
// app/blog/[slug]/page.tsx
export function generateMetadata({ params }: Props): Metadata {
const post = getPostBySlug(params.slug);
return {
openGraph: {
images: [getOgImageUrl(post.title, post.description)],
},
};
}That is the entire integration. Every blog post now gets a unique OG image automatically. When you add a new post, the image just exists. When you change a title, the image updates. No manual step, no build script, no cron job.
Common Mistakes When You Generate OG Images Automatically
A few things I have gotten wrong that you should avoid.
Titles that are too long. If your title is 120 characters, it will get truncated or shrunk to an unreadable font size. Keep OG titles under 60 characters. If your page title is longer, pass a shorter version to the OG image generator.
Not testing on actual platforms. Your image might look perfect in the browser but get cropped differently on LinkedIn versus Twitter versus Slack. Test your generated images with the actual sharing debuggers. Facebook has one. Twitter has one. LinkedIn has one. Use them.
Forgetting twitter:card meta tags. Twitter does not use og:image by default. You need the twitter:card meta tag set to "summary_large_image" and a twitter:image tag. Without these, Twitter shows a tiny thumbnail instead of the full preview.
<!-- Don't forget these -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="{your-og-image-url}" />When Not to Automate
I want to be honest about this. Automatic generation is wrong for some pages.
Your homepage probably deserves a hand-crafted OG image. Same for major landing pages or product announcements. These pages get shared enough that the extra effort of a custom design pays off. A designer can add illustrations, photography, or layout details that no template can replicate.
For everything else, which is the vast majority of pages on most sites, automatic generation is the right call. Blog posts, docs pages, product listings, user profiles, help articles. These need OG images, but spending 10 minutes per page designing them is a waste of time.
The Actual Impact
I added automatic OG images to a documentation site with 340 pages. Before that, about 15 pages had OG images because someone had made them manually at launch and then stopped. The other 325 showed the browser default.
After adding generated images, click-through rates from social shares went up around 35% across the site. That number sounds made up but it tracks with what the research says. Posts with images get significantly more engagement than posts without them. It is not the image quality that matters most. It is having an image at all.
Try it yourself
Pick a template in the playground, grab an API key from the dashboard, and generate OG images automatically for your entire site. Takes about five minutes.
Open Playground