Puppeteer is too big for Vercel: the serverless PDF fix
Chromium blows past Vercel and Lambda size limits. Here's why Puppeteer PDF endpoints fail on serverless, and how @react-pdf/renderer (~2 MB) fixes it without a headless browser.
Your PDF route works on your laptop. You deploy to Vercel or AWS Lambda. The build fails, or the function OOMs, or cold starts stretch past three seconds. The culprit is almost always the same: you shipped a browser to print a document.
This post is the short answer, the real size math, and the fix that fits serverless without giving up correct pagination. For the wider map of PDF approaches in React, see How to generate PDFs in React in 2026.
The short answer
Default Puppeteer + Chromium is roughly 100 MB. Vercel's serverless function bundle limit and Lambda package limits leave little room for that. @react-pdf/renderer is roughly 2 MB, lays out by document structure instead of printing HTML, and deploys on Vercel without a custom Chromium layer. Use Puppeteer only when you must reproduce existing HTML/CSS pixel-for-pixel.
Why does Puppeteer break on Vercel and Lambda?
Headless Chrome is a full browser. Even the slim packages teams reach for first are still heavy:
| Approach | Approx. deploy weight | Typical pain on serverless |
|---|---|---|
| Full Puppeteer + Chromium | ~100–150 MB+ | Exceeds function size; long cold starts |
@sparticuz/chromium + puppeteer-core |
~50 MB | Fits with care, still memory-hungry, cold starts 600ms–1s+ |
@react-pdf/renderer |
~2 MB | Fits default Vercel limits; no browser process |
Vercel hobby and many Lambda configs give you on the order of 50 MB (compressed) for the function bundle before you start special-casing layers and external browsers. Chromium does not fit cleanly. You end up with:
- Bundle size errors at deploy time.
- Memory pressure at runtime (Chromium wants hundreds of MB).
- Cold-start tax every time a new instance boots the browser.
- Font drift between your laptop and the Lambda image.
We walked through the invoice-at-volume cost of that stack in Stop using HTML-to-PDF for invoices. The size limit is the deploy-time version of the same problem.
What is the serverless PDF fix?
Stop printing HTML. Generate the PDF with a document layout engine that runs in-process.
import { renderToBuffer } from "@react-pdf/renderer";
import { InvoiceDocument } from "./InvoiceDocument";
export async function GET() {
const pdf = await renderToBuffer(<InvoiceDocument data={invoice} />);
return new Response(pdf, {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": 'attachment; filename="invoice.pdf"',
},
});
}That handler has no Chromium binary. The dependency graph stays small enough for a normal Next.js Route Handler on Vercel. The same component tree works in Node and (for downloads) in the browser.
Does react-pdf avoid HTML-to-PDF layout bugs too?
Yes, for structured business documents. HTML-to-PDF guesses page breaks from a browser print pipeline. Tables tear mid-row; headings orphan. react-pdf lays out from primitives (Document, Page, View, Text). You still need one discipline for long tables: mark rows wrap={false} so a line item never splits. Full walkthrough: Why your react-pdf table splits across pages (and the fix).
When should you still use Puppeteer?
Keep a headless browser when:
- You must clone an existing print-CSS / marketing page pixel-for-pixel.
- The content is arbitrary rich HTML you cannot map to react-pdf primitives.
- You already run a dedicated render service (Gotenberg, Browserless) outside the serverless function size envelope.
For invoices, receipts, statements, certificates, and reports in a React/Next.js app, react-pdf is the default in 2026.
How do teams ship react-pdf faster?
Hand-writing flexbox-subset layouts is tedious. Two honest paths:
- Write components yourself on top of
@react-pdf/renderer. - Design visually (or with AI), then export owned TSX and render it in your app.
PDFx Builder is that second path: live react-pdf preview, then a .zip of typed source you keep. No lock-in, and customer PII never has to leave your infrastructure when you render locally. Start from a production layout you can preview and export: SaaS subscription invoice, packing slip, or statement of work. Full catalog: template gallery.
If you are comparing hosted PDF APIs that meter every render and keep templates in their editor, see PDFMonkey alternative or CraftMyPDF alternative. For the full trade-off matrix, read react-pdf vs Puppeteer.
Summary
- Chromium on Vercel/Lambda is a size, memory, and cold-start tax.
@react-pdf/renderer(~2 MB) fits serverless and owns layout for data-driven docs.- Fix table tears with
wrap={false}; do not go back to Puppeteer for that. - Export owned code when you want a visual builder without vendor lock-in.
Going deeper
- How to generate PDFs in React in 2026 - the three families and when each wins.
- react-pdf vs Puppeteer: an honest comparison - size, layout, and when HTML-to-PDF still wins.
- Stop using HTML-to-PDF for invoices - cold starts, memory, and font drift at volume.
- Why your react-pdf table splits across pages - the one-prop pagination fix.
- PDFMonkey alternative for React · CraftMyPDF alternative - owned code vs hosted metered APIs.
Build one of these yourself.
Start free at pdfxbuilder.com. Three templates, no credit card.
Open PDFx Builder