Best OG Image Generators (2026): 8 Approaches Compared
Eight ways to generate Open Graph images compared: HTML renderers, Satori, template editors, Cloudinary and Puppeteer. Scored checklist and October 2026 prices.
Disclosure: I build RenderScreenshot, one of the eight options below. To keep that bias visible, here is exactly what this comparison is based on. I ran two engines myself on 2026-10-18: Satori (the engine in @vercel/og) and Chromium, on six identical card templates, with results and timings published below. For the six hosted services, I read their API docs and pricing pages on 2026-10-15 to 2026-10-18 and wrote a minimal request for each from those docs. I have no accounts with them, so those requests are not tested and there are no vendor renders on this page. Where a service wins, I say so.
Every OG image generator does the same job: take some data (a title, an author, a photo) and return a 1200×630 image a crawler can fetch. They differ in two things that matter more than any feature list: where the template lives (your code, a design editor, a URL) and which layout engine draws it (a real browser, a CSS subset, an image CDN). Pick those two and the shortlist is usually one or two names.
The eight options at a glance
| Option | Template lives in | Layout engine | og:image-ready URL | Sync response | Free tier or cost to start |
|---|---|---|---|---|---|
| RenderScreenshot | Your HTML (URL or string) | Chromium | Signed URL, all plans | Yes | 150 credits/month |
| HTML/CSS to Image (HCTI) | Your HTML, or their templates | Chrome | Hosted image URL after an API call; templated URLs | Yes (JSON with URL) | 50 images/month |
| ScreenshotOne | Your HTML or URL | Chromium | Signed links | Yes | 100/month |
@vercel/og / next/og |
JSX in your repo | Satori + resvg | A route on your site | Yes | Free library; your Vercel plan |
| Satori, self-hosted | JSX in your repo | Satori + resvg | A route you host | Yes | Free; your compute |
| Bannerbear | Their visual editor | Their renderer | Instant URLs (Scale plan and above) | Async by default; sync endpoint | 30 trial credits |
| Placid | Their visual editor | Their renderer | URL API with optional signing | Async by default; create_now |
See plans |
| Cloudinary | A base image plus URL transformations | Image CDN | The transformation URL is the image | Yes | 25 credits/month |
| Puppeteer or Playwright, self-hosted | Your HTML | Chromium | A route you host | Yes | Free; your servers |
(That is nine rows because self-hosted Satori and @vercel/og are the same engine with different hosting; I count them as one approach.)
Best for
- Cards that match your site, owned by developers: an HTML renderer. RenderScreenshot if you want signed
og:imageURLs and card presets, HCTI if you want image URLs that stay hosted without running storage, ScreenshotOne if you also need its wider screenshot option list. - Next.js on Vercel with simple layouts:
@vercel/og. Nothing to sign up for, and fast. - Marketing teams that edit designs without engineers: Placid or Bannerbear. Their editors are the product.
- A photo with a title on top, at very high volume: Cloudinary. If your assets already live there, a text overlay is one URL.
- Full control, no third party: self-hosted Playwright or Puppeteer, if you accept running Chromium in production.
Four questions that decide it
Feature lists hide the decision. These four questions, asked in order, usually leave one or two candidates.
flowchart TD
A{Who changes the card design?} -- Marketers, in a design tool --> B[Bannerbear or Placid]
A -- Developers, in code --> C{Is the card a photo with a title on it?}
C -- Yes, assets already on Cloudinary --> D[Cloudinary text overlays]
C -- No, it is a designed layout --> E{Does the layout fit flexbox and a CSS subset?}
E -- Yes, and you deploy on Vercel --> F["@vercel/og"]
E -- No, or you want your site's CSS --> G{Will you run Chromium yourself?}
G -- Yes --> H[Self-hosted Playwright or Puppeteer]
G -- No --> I[HTML renderer: RenderScreenshot, HCTI or ScreenshotOne]
1. Who changes the design? This matters most, and it is not technical. If the people who own the brand do not write code, a template editor saves a ticket for every tweak, and nothing else on this list gives them that. If developers own it, an editor becomes one more place where the card drifts away from the site.
2. Is the card a layout or a decorated photo? A product photo with a price in the corner is an image-processing job, and an image CDN does it in one URL. A card with a heading, a byline, a logo and a background pattern is a layout job, and layouts are what HTML is for.
3. Does your layout fit Satori? Count the CSS features in your current card. If it is flexbox, solid colours, one or two fonts and some images, Satori will draw it, and @vercel/og costs you nothing extra on Vercel. If you use grid, filters, blend modes, variable fonts or a shared component library, expect to rewrite those parts for Satori, and check every render by eye, because it will not report what it skipped.
4. Will you operate Chromium? Self-hosting is cheapest per image and most expensive in attention. A sudden spike in shares means a spike in browser processes. If you already run browsers for other reasons, add cards to that fleet. If not, an HTML renderer API is the same engine without the pager.
The last branch has three names. Among them, the deciding factors are small but real: signed URLs and presets (RenderScreenshot), permanently hosted images (HCTI), or the broadest screenshot option list (ScreenshotOne).
What I tested: Satori against Chromium
The biggest technical split in this market is between real browsers and Satori. Five of the eight options render with Chromium; @vercel/og and self-hosted Satori use their own layout engine; Bannerbear, Placid and Cloudinary use renderers of their own that you never write CSS for.
To show what that split means, I wrote six cards twice, once as a Satori element tree and once as HTML, and rendered each with [email protected] plus @resvg/[email protected], and with Chromium from [email protected]. Node 22.18, Apple M1 Pro, 15 renders per template, 2026-10-18.
| Template | Satori | Chromium |
|---|---|---|
| Flexbox column (section, title, byline) | Correct | Correct |
CSS grid, 2fr 1fr columns |
No error; columns not split 2:1 | Correct |
<style> block with a class |
Correct | Correct |
| Two-line clamp with ellipsis | Correct | Correct |
| Emoji and Japanese text, no extra fonts given | No error; boxes for each glyph | Correct (system fonts present) |
backdrop-filter: blur() glass panel |
No error; panel opaque dark grey | Correct |
Median render time per card was 164 to 226 ms for Satori plus resvg and 16 to 117 ms for a warm Chromium page; launching Chromium took 404 ms once. The full table, with p95 values, file sizes and the caveats (font parsing on every Satori call, warm browser), is on the Vercel OG alternative page.
What I take from it: Satori is not slow and not broken. It is a different engine, and when it does not support something, it tends to draw something else instead of failing. If your cards stay inside its subset, it is a fine choice. If your designers write normal CSS, a browser-based option saves you from finding the differences in production.
Scored checklist
Each criterion is scored from the vendor's own docs (checked 2026-10-15 to 2026-10-18), not from my opinion. "Yes" means documented, "No" means documented as unsupported or absent, "Partial" is explained in the vendor's section, and "Not checked" means I did not find it in the docs I read.
| Criterion | RenderScreenshot | HCTI | ScreenshotOne | @vercel/og | Bannerbear | Placid | Cloudinary | Self-hosted Chromium |
|---|---|---|---|---|---|---|---|---|
| Full browser CSS | Yes | Yes | Yes | No (subset) | No (editor) | No (editor) | No (overlays) | Yes |
| Templates in your repo | Yes | Yes | Yes | Yes | No | No | Partial | Yes |
| Visual template editor | No | Partial | No | No | Yes | Yes | No | No |
| URL that can go straight into og:image | Yes | Partial | Yes | Yes | Partial (plan) | Yes | Yes | Yes |
| Image URLs that never expire | No (30 days) | Yes | Not checked | Your CDN | Yes | Yes | Yes | Your storage |
| Also captures live web pages | Yes | Yes | Yes | No | Partial (v2) | Partial (layer) | No | Yes |
| No infrastructure to run | Yes | Yes | Yes | On Vercel | Yes | Yes | Yes | No |
| Free monthly allowance | Yes | Yes | Yes | n/a | Trial only | See plans | Yes | n/a |
RenderScreenshot loses two rows here: there is no visual editor, and CDN links expire after at most 30 days unless you copy files into your own bucket. If either matters more to you than full CSS, pick a tool that wins that row.
Prices at three volumes
Monthly list prices in USD, as shown on each vendor's pricing page on the date given. "Fresh cards" means renders. With caching, a card is rendered far less often than it is fetched.
| Option | 1,000 fresh cards | 10,000 fresh cards | 100,000 fresh cards | Checked |
|---|---|---|---|---|
| RenderScreenshot | $10 (Starter, 2,500 credits) | $40 (Growth, 12,000 credits) | Above Scale's 60,000 credits ($100); contact us | Pricing, 2026-10-18 |
| HCTI | $14 (Basic, 1,000) | $69 (Basic, 10,000 tier) | $375 (Pro, 100,000 tier) | Pricing page, 2026-10-18 |
| ScreenshotOne | $17 (Basic, 2,000) | $79 (Growth, 10,000) | $259 for 50,000 (Scale) plus 50,000 extra at the listed $0.004 each | Pricing page, 2026-10-18 |
| @vercel/og | Your Vercel function usage | Your Vercel function usage | Your Vercel function usage | Vercel pricing |
| Bannerbear | $49 (Automate, 1,000 credits) | $149 (Scale, 10,000 credits) | From $299 for 50,000 (Enterprise); higher tiers priced on the page | Pricing page, 2026-10-18 |
| Placid | Plans with 500, 2,500, 25,000 and 100,000 credits a month | Pricing page, prices not readable in our text capture, 2026-10-18 | ||
| Cloudinary | Free plan: 25 credits; 1 credit = 1,000 transformations or 1 GB of bandwidth | Plus: $99 for 225 credits | Pricing page, 2026-10-18 | |
| Self-hosted | Your server and your time |
Two billing rules change these numbers more than the list price:
- Are repeat fetches billed? A card shared in a busy Slack workspace is fetched many times. With RenderScreenshot, a signed URL that includes
cache_available=truereturns the earlier render for 0 credits. On Cloudinary, the transformation is cached, but bandwidth still draws credits. Check how your vendor counts a repeat. - Are formats and scales multiplied? Bannerbear's docs say one render costs one credit per format, times the scale. Ask for PNG and JPG at 2x and you pay four credits.
The options in detail
1. RenderScreenshot
What it is: a screenshot API with presets for social cards. Your card is an HTML page on your site (or an HTML string); the API renders it in Chromium on Cloudflare's browser platform and returns a PNG. A signed URL goes straight into og:image.
Strengths: og_card (1200×630), twitter_card (1200×600) and twitter_card_large (1200×628) presets; signed URLs on every plan; X-Cache-URL on every response, a public CDN link you can store; repeat fetches at 0 credits with cache_available; one credit per render whatever the format; SDKs for Node, Python, Ruby, PHP, Go and Java.
Weaknesses: no visual editor; CDN links expire with the cache, 30 days at most; signed URLs must also expire within 30 days, so long-lived static HTML needs a stored copy (custom storage is on paid plans).
curl -G https://api.renderscreenshot.com/v1/screenshot \ -H "Authorization: Bearer $RS_API_KEY" \ --data-urlencode "url=https://example.com/og/hello-world" \ -d preset=og_card -o card.png
This request matches our GET endpoint reference; it was not sent for this article. The OG image tutorial has the production setup with signed URLs.
2. HTML/CSS to Image (HCTI)
What it is: an HTML-to-image API. You POST HTML and CSS (or a URL) and get back JSON with a hosted image URL.
Strengths: simple API; hosted image URLs that stay live while your account is active, so no storage to run; Google Fonts by name in the request; templates with URL parameters for OG use.
Weaknesses: an API call before you have a URL, unless you use its templates; fewer page-capture options than a screenshot API.
curl -X POST https://hcti.io/v1/image \ -u "$HCTI_USER_ID:$HCTI_API_KEY" \ --data-urlencode 'html=<div class="card">Hello world</div>' \ --data-urlencode 'css=.card{width:1200px;height:630px;background:#0f172a;color:#fff;font:700 64px sans-serif}'
Written from HCTI's API docs, not run. Our HCTI alternative page maps every parameter.
3. ScreenshotOne
What it is: a screenshot API with a long option list. It can render a URL or HTML and supports signed links.
Strengths: many capture options, including video output; signed links; a free monthly allowance; a cache URL header.
Weaknesses: no card presets, so you set the viewport yourself; at 1,000 cards a month you pay for its smallest plan of 2,000.
curl -G https://api.screenshotone.com/take \ -d access_key="$SCREENSHOTONE_ACCESS_KEY" \ --data-urlencode "url=https://example.com/og/hello-world" \ -d viewport_width=1200 -d viewport_height=630 -d format=png -o card.png
Written from ScreenshotOne's public API docs, not run.
4. @vercel/og (and self-hosted Satori)
What it is: Vercel's library that turns JSX into PNG with Satori and resvg. It is built into the Next.js App Router as next/og, and Satori itself runs anywhere Node runs.
Strengths: free; fast to start; no third party; the image is generated next to your data; cache headers set for you on Vercel.
Weaknesses: a CSS subset (flexbox, no grid); Vercel lists a 500 KB bundle limit that includes fonts; fonts must be ttf, otf or woff; unsupported CSS fails silently, as the test above shows.
// app/og/route.tsx import { ImageResponse } from 'next/og'; export async function GET() { return new ImageResponse( <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#0f172a', color: '#f8fafc', fontSize: 64 }}> Hello world </div>, { width: 1200, height: 630 }, ); }
Follows Vercel's OG docs. I rendered the same engine through satori directly; I did not deploy this route. The Vercel OG alternative has the test results.
5. Bannerbear
What it is: a template image platform. Design a template in its editor, then POST modifications to fill named layers. It also does animations, workflows and AI image tools.
Strengths: a visual editor for non-developers; hosted files; Instant URLs for og:image; many output formats including AVIF and PDF.
Weaknesses: async by default (the sync endpoint has a 10-second limit); credits multiply by format and scale; Instant URLs and custom S3 start at the Scale plan; templates live outside your repo.
curl -X POST https://api.bannerbear.com/v5/images \ -H "Authorization: Bearer $BANNERBEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"template": "TEMPLATE_UID", "modifications": {"objects": [{"name": "title", "text": "Hello world"}]}}'
Written from Bannerbear's v5 API reference, not run. The Bannerbear alternative shows how to rebuild a template in HTML and keep the modifications array.
6. Placid
What it is: another template editor with an API. Its URL API builds an image from query parameters, with an optional HMAC signature, which makes it easy to put in og:image.
Strengths: URL API on top of a visual editor; simple credit rule (one image, one credit, per its docs); a browserframe layer can place a website screenshot inside a design.
Weaknesses: templates live in its editor (the Placid alternative shows how to rebuild them as HTML); prices were not readable in our text capture of its pricing page, so check them yourself.
https://api.placid.app/u/{template_uuid}?title[text]=Hello%20world
The URL shape follows Placid's URL API docs: the layer name, then the property in brackets. Not run. Our Bannerbear vs Placid comparison goes deeper on both.
7. Cloudinary
What it is: an image CDN and media platform. With text overlays, the URL itself describes the card: a base image, a size and one or more text layers.
Strengths: no template service at all if your assets are already on Cloudinary; the URL is the image; global CDN delivery.
Weaknesses: layout is limited to what overlays can express (position, font, colour, wrapping width); long titles need careful URL encoding; credits count bandwidth as well as transformations.
https://res.cloudinary.com/<cloud_name>/image/upload/w_1200,h_630,c_fill/l_text:Arial_64_bold:Hello%20world,co_white,w_1000,c_fit/fl_layer_apply,g_west,x_80/<base_image_id>.jpg
Built from Cloudinary's transformation reference; not run against an account. Check the text-overlay rules in Cloudinary's docs for escaping commas and slashes in titles. If you use Cloudinary for website screenshots rather than cards, see the Cloudinary screenshot alternative.
8. Puppeteer or Playwright, self-hosted
What it is: your own Chromium, driven by code, behind a route on your server.
Strengths: full CSS; no vendor; no per-image price.
Weaknesses: you run Chromium in production: memory, crashes, zombie processes, fonts on Linux, a queue for traffic spikes. Each of those is solvable, and each is your job. The Puppeteer alternative page and the guide to scaling Puppeteer screenshots cover what that takes.
import { chromium } from 'playwright'; const browser = await chromium.launch(); try { const page = await browser.newPage({ viewport: { width: 1200, height: 630 } }); await page.goto('https://example.com', { waitUntil: 'load' }); await page.screenshot({ path: 'card.png' }); } finally { await browser.close(); }
I ran this listing against https://example.com on 2026-10-18 with [email protected] (same API as the playwright package; import from playwright-core if you manage browsers yourself). It wrote a 1200×630 PNG.
How to run your own evaluation
Vendor pages, this one included, cannot tell you how your own template renders. A fair evaluation takes an afternoon:
- Pick five real cards, not demo cards: your longest title, a title with emoji, one with non-Latin text, one with a photo, and your plainest one.
- Build the template once per model. For HTML renderers, it is one HTML file. For Satori, the same design as JSX. For editors, rebuild it in the editor.
- Render all five with each candidate and put the results side by side. Look for wrapped titles, missing glyphs and layout shifts. Our Satori vs Chromium script is a starting point for the code-based options.
- Paste one URL from each candidate into a private Slack channel and check the preview. That is what your users see.
- Price it with your real numbers: fresh cards per month, not shares.
If you want a free key for step 3, sign up and use the OG image API with the og_card preset.
Changelog
- 2026-10-18: First version. Satori and Chromium tested on six templates; hosted vendors compared from their docs and pricing pages. Vendor renders and a hosted-latency benchmark are not included yet; they will be added only after a run with real accounts, with the raw data.
Related in this topic
- OG Image API: Dynamic Open Graph Images from HTML Turn any HTML page into an og:image with one signed URL. og_card and twitter_card presets, a public X-Cache-URL, 30-day caching and SDKs in six languages.
- Vercel OG Alternative: When Satori's CSS Runs Out A @vercel/og alternative for cards that need full CSS. We rendered six templates in Satori and Chromium: what broke silently, timings, and a migration path.
- Bannerbear Alternative: HTML Templates Instead of an Editor Moving off Bannerbear? Rebuild a template as HTML, keep your modifications array with a tested Node, Python and Ruby adapter, and compare cost at three volumes.
- Placid Alternative: Rebuild Your Templates as HTML Leaving Placid? Map layers, modifications and the URL API to HTML templates, then switch with a tested adapter in Node, Python and Ruby and a gradual rollout.
- APITemplate.io Alternative: HTML Cards Instead of Editor Moving image templates off APITemplate.io? Map overrides, Direct URLs and download_url to HTML templates, with a tested adapter in three languages.
- Cloudinary Screenshot Alternative: Add-on vs a Dedicated API Cloudinary's URL2PNG add-on next to a dedicated screenshot API: option mapping, signing, CDN delivery and expiry, plus a tested switch in Node, Python and Ruby.