Every website needs Open Graph images for social sharing. But generating them is a pain:
- Puppeteer/Playwright: Spin up a headless browser, render HTML, screenshot it. Slow (~2-5 seconds), heavy (200MB+ Chrome binary), expensive to host.
- Canvas libraries: Write imperative drawing code. No hot reload, no component reuse, painful text layout.
- Manual design: Open Figma for each page. Doesn't scale past 10 pages.
I wanted something simpler. So I built OGPix — an API that generates beautiful OG images from URL parameters in ~50ms.
How It Works
Your og:image meta tag becomes a URL:
<meta property="og:image"
content="https://ogpix-pi.vercel.app/api/og?title=My+Article&theme=dark&key=YOUR_KEY" />
That's it. When anyone shares your link on Twitter, LinkedIn, Slack, or Discord — they see a beautiful preview image generated on the fly.
10 Themes
- Gradient — Bold purple gradient
- Minimal — Clean white background
- Dark — Sleek dark mode
- Sunset — Warm orange tones
- Ocean — Deep blue
- Forest — Nature green
- Mono — High contrast black & white
Branded — Your custom colors
Neon — Cyberpunk neon glow
Warm — Cozy cream & terracotta
Try them all in the interactive playground.
The Tech
- Satori (not Puppeteer) renders React components to SVG, then to PNG
- Runs on Vercel Edge Functions — no cold starts, globally distributed
- Images are CDN cached — same parameters = instant response
- Built with Next.js + Supabase + Stripe
Next.js Example
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
const ogUrl = new URL("https://ogpix-pi.vercel.app/api/og");
ogUrl.searchParams.set("title", post.title);
ogUrl.searchParams.set("description", post.excerpt);
ogUrl.searchParams.set("theme", "dark");
ogUrl.searchParams.set("key", process.env.OGPIX_KEY);
return {
openGraph: {
images: [{ url: ogUrl.toString(), width: 1200, height: 630 }],
},
};
}
Works With Everything
It's just a URL, so it works with any framework:
-
Next.js —
generateMetadata - Astro — frontmatter + layout
- Hugo — partial template
-
WordPress —
wp_headhook -
Plain HTML —
<meta>tag
Pricing
- Free: 100 images/month (with watermark)
- Starter: $9/mo — 5,000 images, no watermark
- Pro: $29/mo — 50,000 images
Most personal sites never leave the free tier.
Try It
- 🎨 Playground — design your image live
- 📖 Docs — full API reference
- 🔑 Get API Key — free, 30 seconds
Would love feedback on what themes or features you'd want. What does your ideal OG image look like?
Top comments (2)
The Puppeteer approach breaks down fast once you need OG images at any real scale — cold start alone can add 2-3 seconds on a serverless function, and the memory footprint makes horizontal scaling expensive.
I had to solve a version of this for a financial data site with 8,000+ stock and ETF pages, each needing its own branded OG image with ticker, company name, and a price sparkline. Puppeteer was the obvious first instinct but the idea of spinning up a headless browser per request was a non-starter. We ended up pre-generating images at deploy time using a Node.js + canvas pipeline and serving them as static assets from CDN — solves the latency problem entirely but means you're regenerating on every deploy rather than on-demand.
The on-demand API approach you've built is the right answer for dynamic content where you can't pre-generate. Curious about your caching layer — are you storing generated images at the CDN edge keyed by parameters, or regenerating on every request? For something serving social sharing previews, a long-lived edge cache probably gets your effective p99 well below 50ms for any URL that's been shared before.
Great question! Yes, images are CDN cached at the edge keyed by the full query string. Same params = instant cache hit (~5ms). TTL is 24h via s-maxage=86400.
Your pre-gen approach is smart for 8K+ known pages. On-demand API shines for dynamic content you can't predict at build time.
I chose Satori over canvas for more consistent text rendering across environments — React JSX → SVG → PNG, no browser needed.