Next.js → PDF API in 5 Minutes
Next.js teams replace brittle headless-Chrome cron jobs with a Cloudflare Worker that renders HTML or live URLs to PDF in under 200 ms. You keep the familiar App Router ergonomics while Stripe meters every render, and your customers receive pixel-perfect invoices on time.
Launch Next.js PDFs in three steps
- Add a server-only fetch helper inside an App Router route handler. Create `app/api/pdf/route.ts` that forwards HTML or URLs to `https://api.yourdomain.com/v1/render` with your API key stored in environment variables. The Worker waits for network idle so SSR and ISR routes render correctly.
- Stream the PDF back to the browser or storage bucket. Use `Response` streaming to send the PDF to the browser, or push the `ArrayBuffer` to S3/R2 for later retrieval and emailing.
- Track usage and upgrade paths automatically. Surface `/v1/usage` in your settings page so teams know when they are close to their included renders. Stripe metered billing handles overage without you writing additional code.
Next.js integration code
Drop this snippet into your project, replace `PDF_API_KEY`, and generate your first PDF. Pair it with the curl request for quick smoke tests.
import type { NextRequest } from "next/server"
const API_URL = "https://api.yourdomain.com/v1/render"
const API_KEY = process.env.PDF_API_KEY!
export async function POST(req: NextRequest) {
const payload = await req.json()
const res = await fetch(API_URL, {
method: "POST",
headers: {
authorization: `Bearer ${API_KEY}`,
"content-type": "application/json"
},
body: JSON.stringify({
url: payload.url,
options: { format: "A4", printBackground: true }
})
})
if (!res.ok) {
throw new Error(`Render failed: ${res.status} ${await res.text()}`)
}
return new Response(res.body, {
headers: {
"content-type": "application/pdf",
"content-disposition": "attachment; filename=invoice.pdf"
}
})
}
Test with curl
curl -X POST https://api.yourdomain.com/v1/render \
-H "authorization: Bearer ${PDF_API_KEY}" \
-H "content-type: application/json" \
-d '{"url":"https://demo.yourdomain.com/nextjs-invoice"}' \
--output invoice.pdf
Common pitfalls & fixes
- Wait for `networkidle` in Playwright to avoid truncating data fetched on the client.
- Store the API key in Vercel environment variables and only call the Worker from server modules.
- [object Object]
FAQ
Does this work with the App Router and React Server Components?
Yes. The Worker call runs server-side. Keep the fetch helper in a Route Handler or server action so secrets stay private.
Can I embed custom headers and footers?
Send the `options.headerTemplate` and `options.footerTemplate` fields with HTML snippets; the Worker forwards them to Playwright's `page.pdf()`.