> ## 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.

# Self-Hosting

> Run PDF generation on your own infrastructure with Puppeteer or Playwright. Use client.render() to get HTML, then convert to PDF yourself.

Skip pdfn Cloud and run the full pipeline yourself. You control the browser, the infrastructure, and the output.

<Note>
  Want managed PDF generation? Use `client.generate()` with [pdfn Cloud](https://console.pdfn.dev) — no infra or browser management needed.
</Note>

## How it works

```text theme={null}
React Component → render() → HTML → Puppeteer/Playwright → PDF
```

1. `render()` converts your React template to self-contained HTML
2. You pass that HTML to Puppeteer or Playwright to generate a PDF

***

## Setup

<Tabs>
  <Tab title="Puppeteer">
    ```bash theme={null}
    npm install @pdfn/react puppeteer
    ```

    ```typescript theme={null}
    import { pdfn } from '@pdfn/react';
    import puppeteer from 'puppeteer';
    import fs from 'node:fs';
    import Invoice from './pdfn-templates/invoice';

    async function generatePdf() {
      const client = pdfn();

      // 1. Render React to HTML
      const { data, error } = await client.render({ react: <Invoice /> });

      if (error) {
        console.error(error.message);
        return;
      }

      // 2. Convert HTML to PDF
      const browser = await puppeteer.launch();
      const page = await browser.newPage();

      await page.setContent(data.html, { waitUntil: 'networkidle0' });
      await page.waitForFunction(() => window.PDFN?.ready === true);

      const pdf = await page.pdf({
        preferCSSPageSize: true,
        printBackground: true,
      });

      await browser.close();

      fs.writeFileSync('invoice.pdf', pdf);
    }

    generatePdf();
    ```
  </Tab>

  <Tab title="Playwright">
    ```bash theme={null}
    npm install @pdfn/react playwright
    ```

    ```typescript theme={null}
    import { pdfn } from '@pdfn/react';
    import { chromium } from 'playwright';
    import fs from 'node:fs';
    import Invoice from './pdfn-templates/invoice';

    async function generatePdf() {
      const client = pdfn();

      // 1. Render React to HTML
      const { data, error } = await client.render({ react: <Invoice /> });

      if (error) {
        console.error(error.message);
        return;
      }

      // 2. Convert HTML to PDF
      const browser = await chromium.launch();
      const page = await browser.newPage();

      await page.setContent(data.html, { waitUntil: 'networkidle' });
      await page.waitForFunction(() => window.PDFN?.ready === true);

      const pdf = await page.pdf({
        preferCSSPageSize: true,
        printBackground: true,
      });

      await browser.close();

      fs.writeFileSync('invoice.pdf', pdf);
    }

    generatePdf();
    ```
  </Tab>
</Tabs>

<Note>
  Add `@pdfn/tailwind` if your templates use Tailwind CSS. See [Styling](/styling).
</Note>

***

## Required PDF options

Always use these two options when calling `page.pdf()`:

```typescript theme={null}
await page.pdf({
  preferCSSPageSize: true,  // Respect page size from template
  printBackground: true,    // Include background colors
});
```

And always wait for pdfn to finish pagination before generating:

```typescript theme={null}
await page.waitForFunction(() => window.PDFN?.ready === true);
```

This ensures fonts are loaded and page breaks are calculated.

***

## Comparison

|            | pdfn Cloud                          | Self-Hosted                   |
| ---------- | ----------------------------------- | ----------------------------- |
| Setup      | Managed — no infra or browser setup | You manage infra and Chromium |
| Scaling    | Auto-scales                         | You handle scaling            |
| Compliance | PDF/A-1b, PDF/A-2b, PDF/A-3b        | Not available                 |
| Cost       | Usage-based pricing                 | Your infrastructure costs     |

***

## Next steps

<CardGroup cols={2}>
  <Card title="Styling" icon="paintbrush" href="/styling">
    Tailwind CSS, custom CSS, and inline styles
  </Card>

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