ogrender vs Vercel OG

Vercel OG (@vercel/og) is free and good for a lot of cases. Here's exactly where it stops, and where it's still the right choice.

What Vercel OG actually is

@vercel/og uses Satori, a layout engine Vercel built specifically for generating images from JSX/HTML, running in an Edge function. It doesn't run a browser. It reimplements a subset of CSS layout in JavaScript, which is what makes it fast and free at the edge, and also what limits it.

Vercel OG (Satori)ogrender
Rendering engineCustom layout engine (Satori)Real headless Chromium
CSS GridNot supportedFull support
FlexboxSupported (its core layout mode)Full support
Gradients, filters, box-shadowPartial, many values silently ignoredFull support
Web fontsManual: fetch the font file yourself, pass it as an ArrayBufferGoogle Fonts via @import/<link>, no manual fetching
EmojiNeeds a Twemoji workaround; no native color emojiNative color emoji, Noto fonts installed
Where it runsVercel Edge Runtime, inside a Next.js (or compatible) appAny stack, over HTTP
CostFree, within your Vercel function limits$6-39/month, flat

Stay on Vercel OG if...

  • You're already deployed on Vercel and your card layout is simple flexbox: a title, maybe an avatar and a footer row.
  • You don't need CSS Grid, real gradients, or pixel-exact parity with your site's actual stylesheet.
  • Free matters more than convenience, and you don't mind fetching and subsetting your own font files.

Switch when...

  • Your card layout needs Grid. Satori doesn't support it, full stop, so any Grid-based design has to be rebuilt in flexbox first.
  • You're not on Vercel, or you want the same render call from a Rails app, a cron job, or a static site generator's build step.
  • You want to reuse your site's real CSS instead of hand-porting styles into Satori's supported subset.
  • Color emoji and arbitrary web fonts matter and you don't want to maintain the Twemoji/font-fetching workarounds yourself.
Both approaches produce a PNG from markup. The difference is what CSS you're allowed to write to get there: see the full parameter list in the docs.