Skip to main content
This page covers Next.js patterns for both routers. For initial setup, see the Quickstart.

API route

Accept a request body and return a PDF:
app/api/invoice/route.tsx
import { pdfn } from '@pdfn/react';
import Invoice from '@/pdfn-templates/invoice';

const client = pdfn();

export async function POST(req: Request) {
  const body = await req.json();

  const { data, error } = await client.generate({
    react: (
      <Invoice
        number={body.invoiceNumber}
        customer={body.customerName}
        total={body.total}
      />
    ),
  });

  if (error) {
    return Response.json(
      { error: error.code, message: error.message },
      { status: error.statusCode || 500 }
    );
  }

  return new Response(data.buffer, {
    headers: { 'Content-Type': 'application/pdf' },
  });
}

Download button

Trigger a PDF download from a client component:
app/page.tsx
'use client';

export default function Page() {
  const download = async () => {
    const res = await fetch('/api/invoice', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        invoiceNumber: 'INV-001',
        customerName: 'Acme Corp',
        total: 1500,
      }),
    });
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'invoice.pdf';
    a.click();
    URL.revokeObjectURL(url);
  };

  return <button onClick={download}>Download PDF</button>;
}

Client components

Templates that use "use client" components (like Recharts or other chart libraries) need the withPdfn() build plugin to pre-bundle them for rendering. If you followed the Quickstart, this is already set up:
next.config.ts
import { withPdfn } from '@pdfn/next';

const nextConfig = { /* your existing config */ };

export default withPdfn()(nextConfig);
The plugin detects templates with "use client" directives and bundles them at build time. No additional configuration needed.

Edge runtime

Edge runtimes (Vercel Edge, Cloudflare Workers) don’t have filesystem access. The withPdfn() build plugin handles this by pre-compiling Tailwind CSS at build time. What to keep in mind on edge:
  • Tailwind works — pre-compiled by withPdfn(), no filesystem needed at runtime
  • Local images and fonts won’t load — use remote URLs or base64 data URIs instead
  • client.generate() works — connects to pdfn Cloud for PDF generation, no local dependencies needed

Project structure

app/
├── api/
│   ├── invoice/route.tsx     # PDF endpoint
│   └── receipt/route.tsx
└── page.tsx                  # UI with download button

pdfn-templates/
├── invoice.tsx               # PDF templates
├── receipt.tsx
├── styles.css                # Tailwind theme (isolated from app styles)
└── components/
    └── Header.tsx            # Shared template components

next.config.ts                # withPdfn() build plugin

Environment variables

.env.local
PDFN_API_KEY=pdfn_live_...
Get your key at console.pdfn.dev. Without PDFN_API_KEY, the client uses localhost:3456 automatically. Run npx pdfn dev alongside your Next.js dev server for local development.

Next steps