PDF generation from Sanity data with NextJS - tool recommendations?

1 replyLast updated: Nov 29, 2025

Anyone have some recommendations for generating PDFs from Sanity data? All the data is also shown as HTML, so I guess I could generate the PDFs from the HTML or straight from Sanity, but not sure which solutions would be best and which tools best suitedEdit: I'm using NextJS
AI Update

There 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.

The Main Approaches

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:

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.

Which Approach to Choose?

Go with Puppeteer if:

Go with React-PDF if:

Go with a service if:

Handling Portable Text

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.

Sanity Functions Alternative

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!

Show original thread
1 reply

Was this answer helpful?

Sanity – Build the way you think, not the way your CMS thinks

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.

Related contributions