Next.js  ·  OG Images

Generate OG Images in Next.js
— without Puppeteer

One fetch call from your App Router route handler. Full HTML & CSS support, deploys on Vercel, no Chromium binary in your bundle.

Start Free — 100 renders/mo View API Docs
The Problem

Next.js OG image solutions all have a catch

Every approach has a different tradeoff. Here's why teams end up looking for something better.

Puppeteer in a route handler
Chromium is ~300 MB. Vercel's serverless function limit is 250 MB. Even if you squeeze it in, cold starts take 2–4 seconds and memory pressure causes crashes under load.
RenderPix — zero binary, full CSS
Your route handler sends HTML and gets a PNG back. No binary in your bundle, no cold-start tax. We keep Chromium warm on our end so your users see images in ~200 ms.
@vercel/og / next/og limitations
Satori only supports a flexbox CSS subset. No CSS grid, no custom properties, no pseudo-elements, no background-clip. Anything beyond a simple layout requires painful rewrites.
Full Chromium rendering
RenderPix renders with real headless Chrome. Grid, custom fonts, CSS variables, gradients, clip-path — if your browser can render it, we can capture it at any resolution.
Code Example

Drop-in route handler for App Router

Add a single file to your Next.js project. Works on Vercel, Netlify, or any Node.js host.

app/og/route.ts
App Router · TypeScript
import { NextRequest } from 'next/server'

export async function GET(req: NextRequest) {
  const { searchParams } = new URL(req.url)
  const title  = searchParams.get('title')  ?? 'Untitled'
  const author = searchParams.get('author') ?? ''

  const html = `
    <div style="
      width:1200px; height:630px;
      background:linear-gradient(135deg,#0f172a 0%,#1e1b4b 100%);
      display:flex; flex-direction:column;
      justify-content:flex-end; padding:72px;
      font-family:'Inter',sans-serif;
    ">
      <div style="color:#818cf8;font-size:18px;margin-bottom:20px;font-weight:500">
        myblog.com
      </div>
      <div style="color:#f8fafc;font-size:56px;font-weight:700;line-height:1.15;max-width:900px">
        ${title}
      </div>
      <div style="color:#94a3b8;font-size:20px;margin-top:28px">
        ${author}
      </div>
    </div>
  `

  const res = await fetch('https://api.renderpix.dev/v1/render', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.RENDERPIX_API_KEY}`,
    },
    body: JSON.stringify({ html, width: 1200, height: 630, format: 'png' }),
  })

  const { url } = await res.json()
  const image = await fetch(url)

  return new Response(image.body, {
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control': 'public, max-age=86400, immutable',
    },
  })
}

Use it in any page: <meta property="og:image" content="/og?title=My+Post&author=Jane" />

How It Works

Three steps from template to og:image

01
Write your HTML template
Design your OG image in plain HTML and CSS — any layout, any font, any gradient. 1200×630 is the standard size. No JSX constraints.
02
Call the API from your route handler
POST your rendered HTML to api.renderpix.dev/v1/render. Inject dynamic values (title, author, date) into the template before sending.
03
Return the image & cache it
Stream the PNG back as a Response with a long Cache-Control header. Vercel's CDN handles the rest — each unique image is generated once.
Comparison

RenderPix vs next/og vs Satori

How the three most common approaches stack up for Next.js projects.

Tool Full CSS support Vercel compatible HTML templates Setup time Cold start
RenderPix Yes Yes Yes < 5 min ~200 ms
@vercel/og Subset only Yes JSX only ~30 min < 50 ms
Satori (standalone) Subset only Yes JSX only ~1 hr < 50 ms
Puppeteer in Lambda Yes No (250 MB limit) Yes ~4 hrs 2–4 s
FAQ

Common questions

Does this work on Vercel?

Yes. Your route handler is a normal serverless function that makes an outbound fetch — no Chromium binary, well under the 250 MB limit. It works on Vercel Hobby, Pro, and Enterprise. The render happens on RenderPix infrastructure, not inside your function.

Does it support the Edge Runtime?

The route handler above uses the Node.js runtime (default for App Router). Edge runtime support is on the roadmap. If you need edge today, you can use the Node.js runtime for the /og route only — add export const runtime = 'nodejs' at the top of the file.

How does caching work?

Add a Cache-Control: public, max-age=86400, immutable header to the response. Vercel's CDN will cache the image at the edge after the first request. For blog posts that never change, the OG image is generated once and served from cache forever. You can also pre-generate images at build time and store them in /public/og/.

What does it cost?

Free tier: 100 renders per month, no credit card required. Starter plan is $9/month for 2,000 renders. Most blogs and marketing sites stay on the free tier indefinitely — OG images are generated once per post and cached. Check the pricing page for full details.

Can I use custom fonts?

Yes. Reference any Google Font or self-hosted font in your HTML template with a standard <link> tag or @font-face declaration. The renderer waits for fonts to load before capturing, so your typography will match exactly what you see in a browser.

Add OG images to Next.js in 5 minutes

Free tier, no credit card. Full HTML and CSS support. Works on Vercel out of the box.