PDF Generation
Generate invoices and reports as PDF files using @react-pdf/renderer.
Generate professional PDF documents from React components in ~10 minutes. Includes a production-ready invoice template.
What's Included
| File | Purpose |
|---|---|
components/features/pdf-generation/invoice-template.tsx | InvoiceDocument component + InvoiceData type |
Setup
1
Install dependencies
pnpm add @react-pdf/renderer2
Create the PDF API route
Create app/api/pdf/invoice/route.ts:
import { renderToBuffer } from "@react-pdf/renderer"
import { NextResponse } from "next/server"
import { getRequiredSession } from "@/lib/auth-utils"
import { InvoiceDocument, type InvoiceData } from "@/components/features/pdf-generation/invoice-template"
export async function GET(req: Request) {
const session = await getRequiredSession()
const invoiceId = new URL(req.url).searchParams.get("id")
if (!invoiceId) return NextResponse.json({ success: false, error: "Invoice ID required" }, { status: 400 })
const invoiceData: InvoiceData = {
invoiceNumber: `INV-${invoiceId}`,
date: new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }),
dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }),
company: { name: "Your Company", address: "123 Business St", city: "San Francisco, CA 94102", email: "billing@yourcompany.com" },
customer: { name: session.user.name ?? "Customer", email: session.user.email ?? "", address: "", city: "" },
items: [{ description: "Pro Plan — Monthly", quantity: 1, unitPrice: 29, total: 29 }],
subtotal: 29,
tax: 0,
total: 29,
}
const buffer = await renderToBuffer(<InvoiceDocument data={invoiceData} />)
return new NextResponse(buffer, {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": `attachment; filename="invoice-${invoiceId}.pdf"`,
},
})
}3
Add a download button
"use client"
import { Button } from "@/components/ui/button"
import { Download } from "lucide-react"
export function DownloadInvoiceButton({ invoiceId }: { invoiceId: string }) {
return (
<Button onClick={() => window.open(`/api/pdf/invoice?id=${invoiceId}`, "_blank")} variant="outline" size="sm">
<Download className="mr-2 h-4 w-4" />
Download Invoice
</Button>
)
}InvoiceData Type
interface InvoiceData {
invoiceNumber: string
date: string
dueDate: string
company: { name: string; address: string; city: string; email: string }
customer: { name: string; email: string; address: string; city: string }
items: Array<{ description: string; quantity: number; unitPrice: number; total: number }>
subtotal: number
tax: number
total: number
notes?: string
}Environment Variables
None required.
Verification
- Install deps and create the API route
- Start dev:
pnpm dev - Navigate to
/api/pdf/invoice?id=001(must be authenticated) - A PDF should download with company header, customer details, line items, and totals