
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeThere are several solid approaches for generating PDFs from Sanity data in Next.js, and I'll walk you through the most popular options the community uses.
1. HTML-to-PDF with Puppeteer (Most Common)
Since you already have HTML rendering, using Puppeteer or Playwright to convert HTML to PDF is probably your best bet. For Next.js on Vercel specifically, you'll want to use:
puppeteer-core (instead of full Puppeteer)@sparticuz/chromium or @sparticuz/chromium-min (provides the Chrome binary for serverless environments)Here's a typical setup in a Next.js App Router route handler (app/api/generate-pdf/route.ts):
import puppeteer from 'puppeteer-core';
import chromium from '@sparticuz/chromium';
export async function POST(request: Request) {
const { slug } = await request.json();
const browser = await puppeteer.launch({
args: chromium.args,
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath(),
headless: chromium.headless,
});
const page = await browser.newPage();
await page.goto(`${process.env.NEXT_PUBLIC_URL}/your-content/${slug}`, {
waitUntil: 'networkidle0',
});
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
});
await browser.close();
return new Response(pdf, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename="${slug}.pdf"`,
},
});
}Check out the @sparticuz/chromium documentation for Vercel-specific configuration details and the Vercel docs on using Puppeteer.
2. React-PDF or PDFKit (Direct Generation)
If you want more control over PDF layout or don't want the overhead of running a headless browser, consider:
With React-PDF, you'd create PDF-specific components and render your Portable Text or Sanity data directly:
import { Document, Page, Text, View } from '@react-pdf/renderer';
import { pdf } from '@react-pdf/renderer';
const MyDocument = ({ sanityData }) => (
<Document>
<Page>
<View>
<Text>{sanityData.title}</Text>
{/* Map your Sanity content here */}
</View>
</Page>
</Document>
);
// In your route handler
const pdfBlob = await pdf(<MyDocument sanityData={data} />).toBlob();3. Third-Party Services
Services like DocRaptor, PDFShift, or Gotenberg can handle the heavy lifting if you don't want to manage Chrome binaries in serverless environments.
Go with Puppeteer if:
Go with React-PDF if:
Go with a service if:
If your Sanity data includes Portable Text, you'll need to serialize it. With Puppeteer, you can use @portabletext/react on your HTML page. With React-PDF, you'll need to write a custom serializer since React-PDF uses its own component system.
Worth mentioning: you could also use Sanity Functions to generate PDFs as part of your content workflow (e.g., auto-generate a PDF when a document is published). This keeps the compute workload inside Sanity's infrastructure and can be triggered by content changes. However, for on-demand user-initiated PDF generation in your Next.js app, a route handler is more straightforward.
The Puppeteer + @sparticuz/chromium approach on Vercel is probably your quickest path to success since you already have HTML rendering set up!
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store