> ## Documentation Index
> Fetch the complete documentation index at: https://pdfn.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> Generate PDFs in Next.js with App Router and Pages Router. Includes API route examples, download buttons, streaming, and edge runtime considerations.

This page covers Next.js patterns for both routers. For initial setup, see the [Quickstart](/quickstart).

## API route

Accept a request body and return a PDF:

<Tabs>
  <Tab title="App Router">
    ```tsx app/api/invoice/route.tsx theme={null}
    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' },
      });
    }
    ```
  </Tab>

  <Tab title="Pages Router">
    ```tsx pages/api/invoice.tsx theme={null}
    import type { NextApiRequest, NextApiResponse } from 'next';
    import { pdfn } from '@pdfn/react';
    import Invoice from '@/pdfn-templates/invoice';

    const client = pdfn();

    export default async function handler(
      req: NextApiRequest,
      res: NextApiResponse
    ) {
      const body = req.body;

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

      if (error) {
        return res.status(error.statusCode || 500).send(error.message);
      }

      res.setHeader('Content-Type', 'application/pdf');
      res.send(data.buffer);
    }
    ```
  </Tab>
</Tabs>

***

## Download button

Trigger a PDF download from a client component:

```tsx app/page.tsx theme={null}
'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](/quickstart), this is already set up:

```ts next.config.ts theme={null}
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

```text theme={null}
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

```bash .env.local theme={null}
PDFN_API_KEY=pdfn_live_...
```

Get your key at [console.pdfn.dev](https://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

<CardGroup cols={2}>
  <Card title="Components" icon="cube" href="/components">
    Document, Page, and layout components
  </Card>

  <Card title="Troubleshooting" icon="triangle-exclamation" href="/troubleshooting">
    Error codes, common issues, and fixes
  </Card>
</CardGroup>
