Tutorial overview
We'll build a Next.js API route that converts HTML to PDF, handle React component rendering, and explore deployment strategies for serverless and traditional hosting.
Next.js makes it straightforward to generate PDFs from HTML on the server. Whether you're building invoice generators, report exporters, or document creators, you can leverage Next.js API routes with Puppeteer to create production-ready PDF endpoints. In this guide, we'll show you how to convert HTML to PDF in Next.js, covering everything from basic setup to advanced server-side rendering techniques.
Why Use Next.js for PDF Generation?
Next.js offers several advantages for PDF generation compared to standalone Node.js scripts:
- API Routes: Built-in serverless functions that handle HTTP requests without additional Express setup.
- React SSR: Render React components server-side and convert them directly to PDF with full styling support.
- Type Safety: TypeScript support out of the box for better developer experience and fewer runtime errors.
- Deployment: Easy deployment to Vercel, AWS, or any Node.js hosting with built-in optimizations.
Prerequisites
Before you start, make sure you have:
- Next.js project set up (v13+ with App Router or Pages Router)
- Node.js installed (v18+ recommended)
- Basic knowledge of React and Next.js API routes
- Understanding of async/await and server-side rendering
Step 1: Install Dependencies
First, install Puppeteer in your Next.js project. Puppeteer will handle the HTML-to-PDF conversion:
npm install puppeteerNote: Puppeteer downloads Chromium automatically. For production deployments, especially on serverless platforms, consider using puppeteer-core with a managed Chrome instance to reduce bundle size.
Step 2: Create an API Route
Create a new API route in your Next.js project. For the App Router (Next.js 13+), create app/api/pdf/route.ts. For Pages Router, create pages/api/pdf.ts:
import { NextRequest, NextResponse } from 'next/server';
import puppeteer from 'puppeteer';
export async function POST(request: NextRequest) {
try {
const { html, url } = await request.json();
if (!html && !url) {
return NextResponse.json(
{ error: 'Either html or url must be provided' },
{ status: 400 }
);
}
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
if (url) {
await page.goto(url, { waitUntil: 'networkidle0' });
} else {
await page.setContent(html, { waitUntil: 'networkidle0' });
}
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
margin: {
top: '20px',
right: '20px',
bottom: '20px',
left: '20px',
},
});
await browser.close();
return new NextResponse(pdf, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="document.pdf"',
},
});
} catch (error) {
console.error('PDF generation error:', error);
return NextResponse.json(
{ error: 'Failed to generate PDF' },
{ status: 500 }
);
}
}Step 3: Render React Components to PDF
One of Next.js's strengths is server-side rendering. You can render React components directly to HTML and convert them to PDF. Here's how to create a reusable PDF generation utility:
import { renderToString } from 'react-dom/server';
import puppeteer from 'puppeteer';
export async function generatePDFFromComponent(Component: React.ComponentType<any>, props: any) {
// Render React component to HTML string
const htmlString = renderToString(<Component {...props} />);
// Wrap in a complete HTML document with styles
const fullHTML = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: system-ui, sans-serif; }
/* Add your component styles here */
</style>
</head>
<body>${htmlString}</body>
</html>`;
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.setContent(fullHTML, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
});
await browser.close();
return pdf;
}Step 4: Handle GET Requests (Optional)
You can also create a GET endpoint that accepts a URL query parameter for quick PDF generation:
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const url = searchParams.get('url');
if (!url) {
return NextResponse.json(
{ error: 'URL parameter is required' },
{ status: 400 }
);
}
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle0' });
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
});
await browser.close();
return new NextResponse(pdf, {
headers: {
'Content-Type': 'application/pdf',
},
});
}Step 5: Deployment Considerations
When deploying Next.js PDF generation to production, especially on serverless platforms like Vercel, consider these optimizations:
- Chrome Binary: Use external Chrome instances or managed services to avoid large bundle sizes.
- Timeout Limits: Be aware of serverless function timeout limits (typically 10-60 seconds).
- Memory Limits: Puppeteer can be memory-intensive; monitor your function's memory usage.
- Caching: Consider caching generated PDFs for frequently requested documents.
Common Issues & Solutions
Here are common challenges when generating PDFs in Next.js and how to solve them:
| Issue | Solution |
|---|---|
| Function timeout on Vercel | Move PDF generation to a background job using a queue system, or upgrade to a Pro plan for longer timeouts. Consider using Edge Functions for lighter operations. |
| Puppeteer bundle too large | Use puppeteer-core and connect to an external Chrome instance, or use a managed PDF service. |
| React styles not rendering | Ensure CSS is inlined or loaded via <style> tags. External stylesheets may not load in headless Chrome. |
| Memory errors in production | Always close the browser instance, limit concurrent PDF generations, and consider using a dedicated PDF service for high-volume applications. |
Conclusion
You now know how to convert HTML to PDF in Next.js. By leveraging API routes and Puppeteer, you can build robust PDF generation endpoints that integrate seamlessly with your Next.js application. Remember to handle errors gracefully, optimize for your deployment platform, and consider caching strategies for better performance.
For a managed solution that handles infrastructure, scaling, and optimization automatically, check out HTML2PDFConverter. It provides a simple API for PDF generation without managing Puppeteer deployments or Chrome binaries.