Loading...
Production-ready invoices, contracts, and reports from React.
Same input, same output, every time.
What you preview locally is what ships in production.
What you preview is what you ship — invoices, receipts, reports, and contracts.
Page breaks, headers, and footers that work exactly as expected.
Same result locally, in CI, and in production. Next.js, Vite, and Node.js.
Hot reload as you edit. Inspect layouts before shipping.
PDF/A archival standards and metadata built in. Meet regulatory requirements.
Build templates with built-in page numbers, headers, footers, and smart breaks.
1import { Document, Page, PageNumber, TotalPages } from "@pdfn/react";
2import { Tailwind } from "@pdfn/tailwind";
3
4/**
5 * Professional Invoice template using Tailwind CSS
6 *
7 * Demonstrates:
8 * - Tailwind CSS styling
9 * - Page footer with PageNumber and TotalPages
10 * - Calculated totals with tax
11 */
12
13const items = [
14 { name: "Enterprise License", description: "Annual subscription — unlimited PDFs", qty: 1, price: 4999 },
15 { name: "API Integration Setup", description: "Custom endpoint configuration", qty: 1, price: 1500 },
16 { name: "Custom Template", description: "Branded invoice template", qty: 2, price: 800 },
17 { name: "Priority Support", description: "24/7 support with 1-hour SLA", qty: 12, price: 99 },
18];
19
20const taxRate = 0.0875;
21
22interface InvoiceProps {
23 number: string;
24}
25
26function Invoice({ number }: InvoiceProps) {
27 const subtotal = items.reduce((sum, item) => sum + item.qty * item.price, 0);
28 const tax = subtotal * taxRate;
29 const total = subtotal + tax;
30
31 const formatCurrency = (amount: number) =>
32 "$" + amount.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
33
34 return (
35 <Document title={`Invoice ${number}`}>
36 <Tailwind>
37 <Page
38 size="A4"
39 margin="1in"
40 footer={
41 <div className="flex justify-between items-center text-xs text-gray-500 border-t border-gray-200 pt-3">
42 <div>
43 PDFN • billing@pdfn.dev • +1 (415) 555-0132
44 </div>
45 <div>
46 Page <PageNumber /> of <TotalPages />
47 </div>
48 </div>
49 }
50 >
51 {/* Header */}
52 <div className="flex justify-between items-start mb-8">
53 <div>
54 <img src="https://pdfn.dev/logo-dark.svg" alt="Company Logo" className="h-10 mb-2" />
55 <div className="text-xs text-gray-500">548 Market St, Suite 835, San Francisco, CA 94104</div>
56 </div>
57 <div className="text-right">
58 <div className="text-3xl font-bold text-gray-900 tracking-tight">INVOICE</div>
59 <div className="text-lg font-semibold text-gray-600 mt-1">{number}</div>
60 </div>
61 </div>
62
63 {/* Invoice Details & Bill To */}
64 <div className="flex justify-between mb-8">
65 <div>
66 <div className="text-xs font-semibold text-gray-500 uppercase mb-2">Bill To</div>
67 <div className="text-sm font-semibold text-gray-900">Acme Corporation</div>
68 <div className="text-sm text-gray-600 mt-0.5">456 Enterprise Blvd, Suite 100</div>
69 <div className="text-sm text-gray-600">Austin, TX 78701</div>
70 </div>
71 <div className="text-right">
72 <table className="ml-auto text-sm">
73 <tbody>
74 <tr>
75 <td className="text-gray-500 pr-4 py-0.5">Invoice Date:</td>
76 <td className="text-gray-900 py-0.5">January 15, 2026</td>
77 </tr>
78 <tr>
79 <td className="text-gray-500 pr-4 py-0.5">Due Date:</td>
80 <td className="text-gray-900 py-0.5">February 14, 2026</td>
81 </tr>
82 <tr>
83 <td className="text-gray-500 pr-4 py-1.5 font-semibold">Amount Due:</td>
84 <td className="text-gray-900 py-1.5 font-bold text-lg">{formatCurrency(total)}</td>
85 </tr>
86 </tbody>
87 </table>
88 </div>
89 </div>
90
91 {/* Items Table */}
92 <table className="w-full mb-6 border-collapse">
93 <thead>
94 <tr className="bg-gray-800 text-white">
95 <th className="text-left py-3 px-4 text-xs font-semibold uppercase">Description</th>
96 <th className="text-center py-3 px-4 text-xs font-semibold uppercase w-16">Qty</th>
97 <th className="text-right py-3 px-4 text-xs font-semibold uppercase w-24">Rate</th>
98 <th className="text-right py-3 px-4 text-xs font-semibold uppercase w-28">Amount</th>
99 </tr>
100 </thead>
101 <tbody>
102 {items.map((item, i) => (
103 <tr key={i} className={i % 2 === 0 ? "bg-white" : "bg-gray-50"}>
104 <td className="py-3 px-4 border-b border-gray-100">
105 <div className="font-medium text-gray-900 text-sm">{item.name}</div>
106 {item.description && (
107 <div className="text-xs text-gray-500 mt-0.5">{item.description}</div>
108 )}
109 </td>
110 <td className="text-center py-3 px-4 text-gray-700 text-sm border-b border-gray-100">
111 {item.qty}
112 </td>
113 <td className="text-right py-3 px-4 text-gray-700 text-sm border-b border-gray-100">
114 {formatCurrency(item.price)}
115 </td>
116 <td className="text-right py-3 px-4 font-medium text-gray-900 text-sm border-b border-gray-100">
117 {formatCurrency(item.qty * item.price)}
118 </td>
119 </tr>
120 ))}
121 </tbody>
122 </table>
123
124 {/* Totals */}
125 <div className="flex justify-end mb-8">
126 <table className="w-64 text-sm">
127 <tbody>
128 <tr>
129 <td className="py-2 text-gray-600">Subtotal</td>
130 <td className="py-2 text-right text-gray-900">{formatCurrency(subtotal)}</td>
131 </tr>
132 <tr>
133 <td className="py-2 text-gray-600">Tax ({(taxRate * 100).toFixed(2)}%)</td>
134 <td className="py-2 text-right text-gray-900">{formatCurrency(tax)}</td>
135 </tr>
136 <tr className="border-t-2 border-gray-800">
137 <td className="pt-3 pb-2 font-bold text-gray-900 text-base">Total Due</td>
138 <td className="pt-3 pb-2 text-right font-bold text-gray-900 text-lg">
139 {formatCurrency(total)}
140 </td>
141 </tr>
142 </tbody>
143 </table>
144 </div>
145
146 {/* Notes */}
147 <div className="bg-gray-50 p-4 rounded-lg">
148 <div className="text-xs font-semibold text-gray-700 uppercase mb-1">Notes</div>
149 <div className="text-sm text-gray-600">
150 Thank you for choosing PDFN! Payment is due within 30 days.
151 </div>
152 </div>
153 </Page>
154 </Tailwind>
155 </Document>
156 );
157}
158
159Invoice.PreviewProps = {
160 number: "INV-2026-001",
161} satisfies InvoiceProps;
162
163export default Invoice;
164No code changes between environments.