# Next.js PDF Invoice Route Handler Pattern

> Server-side PDF generation pattern for Next.js App Router using React streaming

A clean production recipe for generating and serving PDF invoices via a Next.js route handler. It combines Zod query validation, database fetching, and server-side React PDF rendering into a streamed binary response with proper attachment headers.

## How it works

How to render a PDF invoice using Next.js (file: api/download/invoice/route.tsx):

- import { PdfTemplate, renderToStream } from "@midday/invoice"; getInvoiceQuery from @midday/supabase/queries; createClient from @midday/supabase/server; NextRequest from next/server; z from zod.

- export const preferredRegion = \["fra1", "sfo1", "iad1"\];

- paramsSchema = z.object({ id: z.string().uuid(), size: z.enum(\["letter","a4"\]).default("a4"), preview: z.boolean().default(false) });

- GET(req: NextRequest): create admin supabase client; parse URL search params with paramsSchema.safeParse; return 400 "Invalid parameters" on failure.

- Fetch invoice via getInvoiceQuery(supabase, id); return 404 "Invoice not found" if missing.

- const stream = await renderToStream(\<PdfTemplate {...data} size={size} /\>); convert to blob via new Response(stream).blob().

- Headers: Content-Type application/pdf, Cache-Control no-store,max-age=0; if not preview, Content-Disposition attachment with filename \`${data.invoice\_number}.pdf\`.

- Return new Response(blob, { headers }).

Pattern: Zod validation → query → React PDF template rendered to stream → blob response with download/preview headers.

## What it is for

- Generate and download PDF invoices on demand in a Next.js application
- Stream rendered React PDF templates directly to the client without saving files to disk
- Support both inline PDF previews and forced file downloads via query parameters

## Key points

- Schema validation with Zod for invoice ID, page size, and preview toggle
- Configured edge/server preferred regions for reliable rendering
- Direct streaming of React PDF templates using renderToStream
- Conditional Content-Disposition headers for inline preview versus attachment download
- No-cache control headers to prevent stale financial document downloads

## Pricing

Free

## Details

- Type: Tip
- Category: [Development](https://xiry.app/topics/development.md)
- Open source: no
- Platforms: Web, API
- For: Next.js and full-stack React developers
- Alternative to: Puppeteer PDF generation, DocuGenerate API
- Tags: nextjs, react pdf, zod, pdf generation, typescript, route handlers

## Why it was saved

- [@pontusab, 28 October 2024](https://x.com/pontusab/status/1850967042800894172): A concise, production-shaped recipe for server-side PDF generation in Next.js: typed request validation, region preference, streaming, and download/preview headers.

## Related

- [Midday React PDF Invoice Template](https://xiry.app/resources/midday-react-pdf-invoice-template): Open-source React PDF template and Next.js route for rendering invoices (Website, Open source)
- [Starter.diy](https://xiry.app/resources/starter-diy-2164): Full-stack SaaS starter with Next.js 15, Convex, and Clerk Billing (GitHub repo, Open source)
- [@json-render/react-pdf](https://xiry.app/resources/json-render-react-pdf): Render structured JSON specifications into PDFs using React PDF (GitHub repo, Open source)
- [assistant-ui](https://xiry.app/resources/assistant-ui): Open-source TypeScript and React component library for building AI chat interfaces (Web app, Open source)
- [Better Upload](https://xiry.app/resources/better-upload): Open-source file upload library for React connecting to S3-compatible storage (Web app, Open source)
- [Gray UI CRM](https://xiry.app/resources/gray-ui-crm): Open-source CRM data grid and interface template built with Next.js and shadcn/ui (GitHub repo, Open source)

Updated 11 October 2026. Page: https://xiry.app/resources/next-js-pdf-invoice-route-handler-pattern

Make your own library: Xiry takes your saved reels, tweets, Reddit bookmarks and Watch Later videos, makes them searchable and pulls the useful stuff out, like this library. It's invite-only for now; join the waitlist at https://xiry.app/#waitlist.
