---
title: "How to convert HTML to PDF in JavaScript: Six libraries compared (2026)"
canonical_url: "https://www.nutrient.io/blog/html-to-pdf-in-javascript/"
md_url: "https://www.nutrient.io/blog/html-to-pdf-in-javascript.md"
last_updated: "2026-09-15T10:33:31.682Z"
description: "Convert HTML to PDF in JavaScript — client-side, Node.js, or API. Compare html2pdf.js, jsPDF, pdfmake, Puppeteer, Playwright, and Nutrient with code examples."
---

**TL;DR**

- **[Nutrient](#nutrient)** — Best for production apps. Full CSS support, fillable forms, signing, redaction, and managed infrastructure. SDKs for web, iOS, Android, and Node.js.

- **[html2pdf.js](#html2pdfjs)** — Best for quick client-side exports. Zero server setup, but limited CSS support and image-based output.

- **[jsPDF](#jspdf-standalone)** — Best for building PDFs programmatically from data. Selectable text, precise layout control, no HTML input.

- **[pdfmake](#pdfmake)** — Best for structured, data-driven documents. Define layouts in JSON; selectable text, no HTML input.

- **[Puppeteer](#puppeteer)** — Best for Chrome-based server-side rendering with a mature ecosystem. Full CSS3 support, Node.js only.

- **[Playwright](#playwright)** — Best for server-side PDF rendering with headless Chromium, especially if you already use Playwright for testing. Supports complex CSS but requires Node.js infrastructure.

## How to convert HTML to PDF in JavaScript

HTML-to-PDF conversion in JavaScript turns rendered HTML and CSS into a PDF file — either in the browser with a client-side library, on the server with a headless browser, or through a managed API.

To convert HTML to PDF in JavaScript:

1. **Client-side (no server)** — Add html2pdf.js via CDN, select your HTML element, and call `.from(element).save()`.

2. **Server-side (Node.js)** — Use Puppeteer or Playwright to launch a headless browser, call `page.setContent()`, and use `page.pdf()` to generate the file.

3. **Managed API** — Send your HTML to Nutrient’s Processor API and receive a PDF in response, with no infrastructure to manage.

The quickest way to get started is html2pdf.js. Add the library via CDN, select your HTML element, and call `html2pdf().from(element).save()`.

```js

// Include via CDN: https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.12.1/html2pdf.bundle.min.js
const element = document.getElementById('content');
html2pdf().from(element).save('document.pdf');

```

This works for simple documents. For client-side conversion with no server, html2pdf.js or pdfmake is the fastest to set up. For full CSS3 support — including flexbox, grid, and custom fonts — use Puppeteer or Playwright (server-side) or Nutrient (managed API). For fillable PDF forms, page manipulation, or enterprise compliance, Nutrient covers all of these out of the box.

## Six ways to convert HTML to PDF in JavaScript

There are six main approaches, each with different tradeoffs:

1. **HTML-to-image libraries** (html2pdf.js) — Quick setup, converts HTML to canvas, then to PDF

2. **Programmatic builders** (jsPDF, pdfmake) — Build PDFs from data, selectable text

3. **Headless Chrome automation** (Puppeteer) — High fidelity, full CSS support, Chrome-focused

4. **Browser automation with PDF export** (Playwright) — High-fidelity rendering with headless Chromium; PDF generation isn’t supported in Firefox or WebKit

5. **Document platforms** (Nutrient) — SDKs and APIs with viewing, signing, and more

6. **Custom solutions** — Full control but significant development overhead

This guide covers the first five, comparing capabilities, performance, and ideal use cases.

## Open source vs. commercial HTML-to-PDF libraries

Open source libraries (html2pdf.js, pdfmake, Puppeteer, Playwright) are free to use and work well for many use cases. Commercial solutions like Nutrient add features that matter for larger deployments.

| Consideration  | Open source       | Nutrient                     |
| -------------- | ----------------- | ---------------------------- |
| Cost           | Free              | Subscription after free tier |
| Support        | Community forums  | Dedicated support, SLAs      |
| CSS support    | Varies by library | Full CSS3                    |
| Infrastructure | Self-managed      | Managed or self-hosted       |
| Compliance     | DIY               | SOC 2, encryption included   |

Pick based on your scale, support needs, and required features.

## Nutrient

[Nutrient](https://www.nutrient.io/sdk/) is a document processing platform with SDKs for web, iOS, Android,.NET, Java, and Node.js. It handles viewing, annotations, eSignatures, redaction, OCR, and AI-powered extraction in addition to HTML-to-PDF conversion.

Nutrient offers several conversion options:

- **[Document Engine](https://www.nutrient.io/guides/document-engine/pdf-generation.md)** — Self-hosted server API with full CSS support. Converts HTML forms into [fillable PDF fields](https://www.nutrient.io/guides/document-engine/forms/introduction-to-forms/form-fields.md). Chain operations like merging, [watermarking](https://www.nutrient.io/guides/document-engine/editor/watermark.md), and page manipulation in one call.

```bash

curl -X POST http://localhost:5000/api/build \
  -H "Authorization: Token token=<API token>" \
  -F page.html=@/path/to/page.html \
  -F instructions='{ "parts": [{ "html": "page.html" }] }' \
  -o result.pdf

```

- **[Processor API](https://www.nutrient.io/api/)** — Cloud-hosted API for serverless workflows. Supports PDF generation, conversion, OCR, and batch processing.

```js

// Node.js example
// This code requires Node.js. Do not run this code directly in a web browser.

const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')

const formData = new FormData()
formData.append('html', fs.createReadStream('index.html'))

;(async () => {
  try {
    const response = await axios.post('https://api.nutrient.io/processor/generate_pdf', formData, {
      headers: formData.getHeaders({
        'Authorization': 'Bearer your_api_key_here'
      }),
      responseType: "stream"
    })

    response.data.pipe(fs.createWriteStream("result.pdf"))
  } catch (e) {
    const errorString = await streamToString(e.response.data)
    console.log(errorString)
  }
})()

function streamToString(stream) {
  const chunks = []
  return new Promise((resolve, reject) => {
    stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)))
    stream.on("error", (err) => reject(err))
    stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
  })
}

```

- **[.NET SDK](https://www.nutrient.io/guides/dotnet/conversion/html-to-pdf.md)** — Chrome-based rendering for local files and live URLs.

- **Mobile SDKs ([Android](https://www.nutrient.io/guides/android/conversion.md) and [iOS](https://www.nutrient.io/guides/ios/conversion/html-to-pdf.md))** — Generate PDFs from HTML on-device.

- **No-code ([Zapier](https://www.nutrient.io/blog/nutrient-api-zapier-convert-html-to-pdf/) and Power Automate)** — Convert templates and form submissions without code.

### Features not available in open source libraries

- **Form field conversion** — HTML form elements become [fillable PDF fields](https://www.nutrient.io/guides/document-engine/pdf-generation/from-html/fillable-pdf-forms.md) automatically

- **Chained operations** — Merge, watermark, and manipulate pages in a single API call

- **Cross-platform consistency** — Same [rendering engine](https://www.nutrient.io/sdk/solutions/generation/) across [web](https://www.nutrient.io/guides/web/pdf-generation.md), [iOS](https://www.nutrient.io/guides/ios/pdf-generation.md), [Android](https://www.nutrient.io/guides/android/pdf-generation.md), and server

- **[PDF generation](https://www.nutrient.io/sdk/pdf-generation/)** — Generate PDFs from HTML, templates, or data across all platforms

- **Enterprise support** — SLAs, dedicated support, long-term maintenance

- **Compliance** — SOC 2 Type 2 audited, encryption, audit trails

## html2pdf.js

html2pdf.js is a client-side library suited for prototypes and simple applications. It produces image-based output without text selection and can hit browser memory limits on large documents.

The [html2pdf](https://github.com/eKoopmans/html2pdf/blob/master/dist/html2pdf.bundle.min.js) library converts HTML pages to PDFs in the browser. It uses [`html2canvas`](https://html2canvas.hertzen.com/) and [`jsPDF`](https://github.com/parallax/jsPDF) under the hood. `html2canvas` renders an HTML page into a canvas element and turns it into a static image. `jsPDF` then takes the image and converts it to a PDF file.

See [how to convert HTML to PDF using React](https://www.nutrient.io/blog/how-to-convert-html-to-pdf-using-react.md) for using `jsPDF` in a React app.

### Installation options

Install `html2pdf.js` in one of three ways:

- **CDN** — Add a `script` tag to your HTML

```html

<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.12.1/html2pdf.bundle.min.js"></script>

```

- **npm** — `npm install html2pdf.js`

- **Manual download** — Grab the [bundle from GitHub](https://github.com/eKoopmans/html2pdf.js/blob/master/dist/html2pdf.bundle.min.js) and include it with a script tag

```html

<script src="html2pdf.bundle.min.js"></script>

```

### Convert HTML to PDF using html2pdf

Define a `generatePDF()` function to get the HTML element and convert it to PDF. Call this function from a download button:

```html

<!DOCTYPE html>
<html>
  <head>
    <!-- html2pdf CDN link -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.12.1/html2pdf.bundle.min.js"></script>
  </head>
  <body>
    <button id="download-button">Download as PDF</button>
    <div id="invoice">
      <h1>Our Invoice</h1>
    </div>

    <script>
      const button = document.getElementById("download-button");

      function generatePDF() {
        // Choose the element that your content will be rendered to.
        const element = document.getElementById("invoice");
        // Choose the element and save the PDF for your user.
        html2pdf().from(element).save();
      }

      button.addEventListener("click", generatePDF);
    </script>
  </body>
</html>

```

This example renders only the `h1` element, but you can render any HTML element, including images and tables.

To test this example locally, run a quick static server with:

```bash

npx serve -l 4111.

```

Then open [http://localhost:4111](http://localhost:4111) in your browser to interact with the PDF download button.![html2pdf.js converting an HTML element to a downloadable PDF in the browser](@/assets/images/blog/2019/html-to-pdf-in-javascript/html2pdf-example.png)

### Open PDF in new tab (instead of downloading)

Sometimes you want to display a PDF in a new browser tab rather than trigger a download. Use `outputPdf('blob')` to get a Blob. Then create a URL:

```js

function openPDFInNewTab() {
  const element = document.getElementById("invoice");
  html2pdf().from(element).outputPdf("blob").then((blob) => {
      const url = URL.createObjectURL(blob);
      window.open(url, "_blank");
    });
}

```

This works well for previews or when users want to review before saving.

### Advanced example: Downloading an invoice as a PDF

You can use an [invoice template like this one](https://github.com/sparksuite/simple-html-invoice-template/blob/master/invoice.html) as your HTML source. You can also generate the HTML for your own invoice on your backend.

As in the previous example, the `generatePDF()` function downloads the invoice as a PDF. This time, the invoice template renders to the `div` element.

The end result will look like what’s shown below.![Preview of a fully rendered HTML invoice](@/assets/images/blog/2019/html-to-pdf-in-javascript/invoice-preview.png)

### Common issues with html2pdf.js in production

**Typical problems** — Blurry output, memory crashes with large documents, and inconsistent mobile rendering. Nutrient Web SDK addresses these issues while still supporting client-side operation.

## jsPDF (standalone)

[jsPDF](https://github.com/parallax/jsPDF) is the underlying library that html2pdf.js uses, but you can use it directly for more control. It’s ideal when you need to build PDFs programmatically with text, images, and shapes — without converting HTML.

### Installation

Install jsPDF via npm:

```bash

npm install jspdf

```

Or via CDN:

```html

<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/4.2.1/jspdf.umd.min.js"></script>

```

### Basic example with text and images

This example builds a simple invoice from text, lines, and positioned elements:

```js

const { jsPDF } = window.jspdf;

function generatePDF() {
  const doc = new jsPDF();

  // Add text.
  doc.setFontSize(22);
  doc.text("Invoice", 20, 20);

  doc.setFontSize(12);
  doc.text("Date: February 6, 2026", 20, 35);
  doc.text("Total: $150.00", 20, 45);

  // Add a line.
  doc.setDrawColor(0);
  doc.line(20, 50, 190, 50);

  // Add items.
  doc.text("Item 1 - Widget", 20, 60);
  doc.text("$50.00", 170, 60, { align: "right" });

  doc.text("Item 2 - Gadget", 20, 70);
  doc.text("$100.00", 170, 70, { align: "right" });

  doc.save("invoice.pdf");
}

```

### When to use jsPDF vs. html2pdf.js

| Use jsPDF when...                              | Use html2pdf.js when...          |
| ---------------------------------------------- | -------------------------------- |
| Building PDFs from data (not HTML)             | Converting existing HTML layouts |
| You need precise positioning                   | Quick prototypes with HTML       |
| Adding images, shapes, tables programmatically | CSS styling is important         |
| Smaller file sizes matter                      | Simplicity over control          |

## pdfmake

pdfmake works well for data-driven documents and reports. It requires manual configuration and doesn’t accept HTML input — you define layouts in JavaScript objects instead.

The [pdfmake](https://github.com/bpampuch/pdfmake) library generates PDF documents directly in the browser. It uses a layout and style configuration defined in a JSON-like structure within JavaScript. It’s built on [pdfkit](https://github.com/foliojs/pdfkit).

Unlike `html2pdf.js`, which converts HTML to an image first, `pdfmake` defines document structure in JavaScript, so text stays selectable.

### Installation

Add via CDN:

```html

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/vfs_fonts.min.js"></script>

```

Or, install via npm:

```bash

npm install pdfmake

```

### Generate PDFs from HTML using pdfmake

Define a `generatePDF()` function with your PDF content as a JavaScript object. Use `pdfmake` to create and download the file. The example shows text and a table, but `pdfmake` also supports lists, images, and advanced styling.

```html

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/pdfmake.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.2.12/vfs_fonts.min.js"></script>
  </head>
  <body>
    <button onclick="generatePDF()">Download Invoice</button>

    <!-- Move script here so pdfMake is loaded -->

    <script>
      function generatePDF() {
        const docDefinition = {
          content: [
            { text: "Invoice", style: "header" },
            {
              table: {
                body: [
                  ["Item", "Qty", "Price"],
                  ["Product 1", "2", "$10"],
                  ["Product 2", "1", "$20"],
                  ["Total", "", "$40"],
                ],
              },
            },
          ],
          styles: {
            header: { fontSize: 18, bold: true },
          },
        };

        pdfMake.createPdf(docDefinition).download("invoice.pdf");
      }
    </script>
  </body>
</html>

```

You can test the example by running:

```bash

npx serve -l 4111.

```![pdfmake generating a PDF from a JavaScript object definition — invoice table example](@/assets/images/blog/2019/html-to-pdf-in-javascript/pdfmake-example.png)

### Benefits of using pdfmake

- **Selectable text** — Unlike `html2pdf.js`, `pdfmake` preserves text as selectable and copyable.

- **Custom styling** — Define font sizes, colors, and positioning with structured JavaScript.

- **Advanced layouts** — Supports tables, lists, and multicolumn designs.

## Puppeteer

[Puppeteer](https://pptr.dev/) converts HTML to PDF by driving headless Chrome from Node.js. It renders pages with Chrome’s actual engine, so output matches what you see in the browser — including flexbox, grid, and custom fonts. It’s the most widely used server-side option, with a large ecosystem of guides and integrations.

### Installation

Install Puppeteer via npm:

```bash

npm install puppeteer

```

The install downloads a compatible Chrome build automatically.

### Convert HTML to PDF using Puppeteer

Launch a browser, set the page content (or navigate to a URL with `page.goto()`), and call `page.pdf()`:

```js

// index.js
const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // Set HTML directly, or use `page.goto("https://example.com", { waitUntil: "networkidle0" })` for live URLs.
  await page.setContent("<h1>Our Invoice</h1>");

  // Generate the PDF with backgrounds preserved.
  await page.pdf({
    path: "invoice.pdf",
    format: "A4",
    printBackground: true,
  });

  await browser.close();
})();

```

Run `node index.js` to generate `invoice.pdf`. For headers, footers, and page numbers, pass `displayHeaderFooter: true` with `headerTemplate`/`footerTemplate` options to `page.pdf()`.

### Puppeteer vs. Playwright

Both drive real browsers and produce near-identical PDF output for Chrome rendering. Choose Puppeteer if you’re standardizing on Chrome and want the larger ecosystem; choose Playwright if you also test across Firefox and WebKit or already use it for end-to-end testing. Either way, you own the server infrastructure — browser processes are memory-hungry, so budget for concurrency limits and queue management in production.

## Playwright

Playwright provides high-fidelity rendering with full CSS support. It requires server infrastructure and ongoing maintenance.

[Playwright](https://github.com/microsoft/playwright) automates Chromium, Firefox, and WebKit, but its `page.pdf()` method only supports headless Chromium. Unlike html2pdf, this PDF conversion runs on your server.

### Installation

Create a new project and install [Playwright](https://www.npmjs.com/package/playwright):

```bash

mkdir playwright-pdf-generation && cd playwright-pdf-generation
npm init --yes
npm install playwright

```

Before using Playwright, make sure the required browser binaries are installed by running:

```bash

npx playwright install

```

### Basic PDF example

Create an `index.js` file that requires Playwright, launches a browser session, goes to your invoice page, and saves the PDF file:

```js

// index.js

// Require Playwright.
const { chromium } = require("playwright");

(async function () {
  try {
    // Launch a new browser session.
    const browser = await chromium.launch();
    // Open a new page.
    const page = await browser.newPage();

    // Set the page content.
    await page.setContent("<h1>Our Invoice</h1>");

    // Generate a PDF and store it in a file named `invoice.pdf`.
    await page.pdf({ path: "invoice.pdf", format: "A4" });

    await browser.close();
  } catch (e) {
    console.error(e);
  }
})();

```

Running `node index.js` generates `invoice.pdf` locally. To let users download PDFs, set up a Node server with the `http` module. The server listens on `/generate-pdf`, renders the page with Playwright, and returns the PDF to the browser.

To do this, omit the `path` option in `page.pdf()` to get a buffer, set the response header to `application/pdf`, and send the buffer as the response. For all other routes, return a simple HTML page with a link to trigger the PDF download:

```js

// index.js

const { chromium } = require("playwright");
const http = require("http");

// Create an instance of the HTTP server to handle the request.
http.createServer(async (req, res) => {
    if (req.url === "/generate-pdf") {
      // Making sure to handle a specific endpoint.
      const browser = await chromium.launch();
      const page = await browser.newPage();

      // Set the content directly or navigate to an existing page.
      await page.setContent(`
      <!DOCTYPE html>
      <html>
      <head>
        <title>Invoice</title>
      </head>
      <body>
        <h1>Our Invoice</h1>
        <p>Details about the invoice...</p>
      </body>
      </html>
    `);

      // By removing the `path` option, you'll receive a `Buffer` from `page.pdf`.
      const buffer = await page.pdf({ format: "A4" });

      await browser.close();

      // Set the content type so the browser knows how to handle the response.
      res.writeHead(200, { "Content-Type": "application/pdf" });
      res.end(buffer);
    } else {
      // Respond with a simple instruction page.
      res.writeHead(200, { "Content-Type": "text/html" });
      res.end(
        '<h1>Welcome</h1><p>To generate a PDF, go to <a href="/generate-pdf">/generate-pdf</a>.</p>',
      );
    }
  }).listen(3000, () => {
    console.log("Server is running on http://localhost:3000");
  });

```

Open `localhost:3000` in your browser to see the instruction page. Navigate to `/generate-pdf` to open the PDF with the invoice.![Playwright converting an HTML page to PDF server-side in Node.js — browser output](@/assets/images/blog/2019/html-to-pdf-in-javascript/playwright-example.png)

## Common HTML-to-PDF conversion issues and fixes

#### html2pdf.js

- Blurry output — Use high-resolution images and scale properly.

- Limited CSS — Limited styling; avoid modern layout features like flexbox or grid.

- Large files — Optimize HTML and assets.

#### pdfmake

- Layout complexity — Manual configuration required; break documents into smaller blocks when needed.

- Font issues — Ensure custom fonts are embedded correctly.

#### Puppeteer

- Missing backgrounds — Pass `printBackground: true` to `page.pdf()`; backgrounds are off by default.

- Content not loaded — Use `waitUntil: "networkidle0"` when navigating to pages with dynamic content.

- Memory in production — Browser instances are heavy; reuse browsers and limit concurrency.

#### Playwright

- Complex setup — More involved than client-side tools; requires server infrastructure.

- Slow rendering — Minimize dynamic scripts or unnecessary content before generating PDFs.

#### Nutrient

- License validation — Confirm valid key and domain configuration.

- Large documents — Use streaming API for >100MB.

- Custom fonts — Upload to Document Engine for consistency.

## HTML-to-PDF library comparison

| Feature              | html2pdf.js                          | jsPDF                                | pdfmake                              | Puppeteer                            | Playwright                           | Nutrient                             |
| -------------------- | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ | ------------------------------------ |
| **Ease of use**      | Very easy                            | Easy                                 | Moderate                             | Moderate                             | Moderate                             | Easy                                 |
| **Installation**     | Simple (CDN, npm)                    | Simple (CDN, npm)                    | Simple (CDN, npm)                    | npm                                  | npm                                  | SDK/API                              |
| **Runs in browser**  |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![No](@/icons/misc/cross-red.svg)    |![No](@/icons/misc/cross-red.svg)    |![Yes](@/icons/misc/check-green.svg) |
| **Text selection**   |![No](@/icons/misc/cross-red.svg)    |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |
| **CSS/HTML support** | Limited                              | None                                 | None                                 | Full                                 | Full                                 | Full                                 |
| **Customization**    | Low                                  | High                                 | High                                 | High                                 | High                                 | High                                 |
| **PDF quality**      | Medium (image-based)                 | High (vector)                        | High (structured)                    | Very high (Chrome)                   | Very high (browser)                  | Very high (Chrome-based)             |
| **Node.js support**  |![No](@/icons/misc/cross-red.svg)    |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |![Yes](@/icons/misc/check-green.svg) |
| **Best for**         | Quick HTML exports                   | Data-driven PDFs                     | JSON-based layouts                   | Chrome server rendering              | Complex CSS layouts                  | Production apps, compliance          |
| **Support**          | Community                            | Community                            | Community                            | Community                            | Community                            | Enterprise SLA                       |

**Featured Content**

**Convert HTML to PDF with annotations**

Nutrient SDK includes annotations, form filling, and custom output options.

[Get started with Nutrient Web SDK](https://www.nutrient.io/sdk/web/getting-started.md)

[Start your free trial](https://www.nutrient.io/sdk/document-engine/getting-started/curl.md) to test Nutrient’s HTML-to-PDF conversion.

## Which HTML-to-PDF JavaScript library should you use?

Choose based on your specific use case:

| Use case                | Recommended             | Why                                          |
| ----------------------- | ----------------------- | -------------------------------------------- |
| **Quick prototype**     | html2pdf.js             | Fastest setup, no server needed              |
| **Invoices from data**  | jsPDF or pdfmake        | Build PDFs programmatically, selectable text |
| **Complex CSS layouts** | Puppeteer or Playwright | Full browser rendering, pixel-perfect        |
| **Production app**      | Nutrient                | Managed infrastructure, support, compliance  |
| **Fillable forms**      | Nutrient                | Only option with form field conversion       |
| **Mobile apps**         | Nutrient                | iOS/Android SDKs available                   |

**Decision flowchart**

1. Need fillable PDF forms? → **Nutrient**

2. Running on mobile? → **Nutrient** (iOS/Android SDKs)

3. Need full CSS support? → **Puppeteer** or **Playwright** (self-hosted) or **Nutrient** (managed)

4. Building from data, not HTML? → **jsPDF** or **pdfmake**

5. Simple prototype? → **html2pdf.js**

## Related posts

- [How to convert HTML to PDF using React](https://www.nutrient.io/blog/how-to-convert-html-to-pdf-using-react.md)

- [How to generate PDFs in React with React to PDF](https://www.nutrient.io/blog/how-to-create-pdfs-with-react-to-pdf.md)

- [How to generate PDF reports from HTML in Node.js](https://www.nutrient.io/blog/how-to-generate-pdf-reports-from-html-in-nodejs/)

- [How to generate PDF reports from HTML in Python](https://www.nutrient.io/blog/how-to-generate-pdf-reports-from-html-in-python.md)

- [How to convert HTML to PDF in C# using Nutrient](https://www.nutrient.io/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-csharp.md#method-2--html-to-pdf-in-c-using-nutrient-api)

- [Top 10 ways to convert HTML to PDF](https://www.nutrient.io/blog/top-ten-ways-to-convert-html-to-pdf.md)

- [HTML to PDF with Nutrient Document Engine](https://www.nutrient.io/blog/top-ten-ways-to-convert-html-to-pdf.md#10-nutrient-document-engine)

- [HTML-to-PDF API guide](https://www.nutrient.io/api/html-to-pdf-api/)

- [How to display a PDF in HTML: Six easy ways](https://www.nutrient.io/blog/open-pdf-in-your-web-app.md)

- [How to convert HTML to PDF using html2pdf.js](https://www.nutrient.io/blog/how-to-convert-html-to-pdf-using-html2pdf.md)

## FAQ

#### What is the best JavaScript library for converting HTML to PDF?

It depends on your needs. For simple client-side conversion, use html2pdf.js. For structured documents from data, use pdfmake. For full CSS support on a server, use Puppeteer or Playwright. For managed infrastructure with additional features, use Nutrient.

#### Can I convert HTML to PDF in the browser without a server?

Yes. html2pdf.js and pdfmake both run entirely in the browser. html2pdf.js converts HTML elements directly, while pdfmake generates PDFs from JavaScript objects. Neither requires a backend server.

#### Why is my HTML-to-PDF conversion output blurry?

html2pdf.js creates image-based PDFs, which can look blurry at certain zoom levels. For sharper output, use Playwright or Nutrient, which render actual text and vector graphics instead of images.

#### How do I preserve CSS styles when converting HTML to PDF?

html2pdf.js has limited CSS support. For full CSS3 compatibility, including flexbox and grid, use Puppeteer or Playwright (server-side) or Nutrient. All three use real browser engines to render your styles accurately.

#### What’s the difference between client-side and server-side PDF generation?

Client-side (html2pdf.js, pdfmake) runs in the browser and needs no server, but it’s limited by browser memory and capabilities. Server-side (Playwright, Nutrient) runs on a server and handles larger documents and complex CSS, but it requires infrastructure.

#### Can I convert HTML forms into fillable PDF forms?

Most open source libraries don’t support this. Nutrient [Document Engine](https://www.nutrient.io/guides/document-engine/pdf-generation.md) converts HTML form elements into [fillable PDF fields](https://www.nutrient.io/guides/document-engine/forms/introduction-to-forms/form-fields.md), including text inputs, checkboxes, and dropdowns.

#### How do I convert HTML to PDF in Node.js?

Puppeteer, Playwright, and Nutrient’s Processor API all handle server-side conversion — see the [Puppeteer](#puppeteer), [Playwright](#playwright), and [Nutrient](#nutrient) sections above for full code examples. For a Node.js-specific walkthrough covering templates, headers, footers, and batching, see [how to generate PDF reports from HTML in Node.js](https://www.nutrient.io/blog/how-to-generate-pdf-reports-from-html-in-nodejs/).

#### How do I add page breaks when converting HTML to PDF in JavaScript?

Add `break-before: page` or `break-after: page` CSS rules to the elements where you want breaks. For example, add `style="break-before: page"` to any `div` or section that should start on a new page. Note that html2pdf.js has limited support for page break CSS — for reliable page break handling, use Playwright or Nutrient, both of which use a full browser rendering engine.

#### Is there a free HTML-to-PDF API?

Nutrient offers a free tier for its [Processor API](https://www.nutrient.io/api/) that lets you convert HTML to PDF without managing infrastructure. Open source options like Playwright are also free but require you to host and maintain your own server. html2pdf.js and pdfmake run entirely in the browser at no cost, but have limitations around CSS support and output quality.
---

## Related pages

- [The business case for accessibility: Five ways it drives enterprise value](/blog/5-ways-accessibility-drives-enterprise-value.md)
- [Accessibility Untangled Why It Matters Guide](/blog/accessibility-untangled-why-it-matters-guide.md)
- [Advanced Techniques For React Native Ui Components](/blog/advanced-techniques-for-react-native-ui-components.md)
- [`vector_store` holds your indexed documents (see the multimodal RAG post](/blog/agentic-rag.md)
- [How to build an AI agent for contract redlining against a compliance playbook](/blog/ai-contract-redlining-compliance-playbook.md)
- [Ai Document Automation Extraction To Action](/blog/ai-document-automation-extraction-to-action.md)
- [Ai Legal Assistant Document Authoring](/blog/ai-legal-assistant-document-authoring.md)
- [Amazon Textract Alternatives](/blog/amazon-textract-alternatives.md)
- [Start (clears any prior buffer), navigate the document, then stop into a file.](/blog/android-faster-pdf-rendering.md)
- [Android Pdf Out Of Memory Handling](/blog/android-pdf-out-of-memory-handling.md)
- [Angular File Viewer Pdf Image Office Files](/blog/angular-file-viewer-pdf-image-office-files.md)
- [Approval Workflow Software](/blog/approval-workflow-software.md)
- [Approvals Matrix](/blog/approvals-matrix.md)
- [Auto Tagging And Document Accessibility In Dotnet Sdk](/blog/auto-tagging-and-document-accessibility-in-dotnet-sdk.md)
- [Simple PII redaction.](/blog/automated-pii-removal.md)
- [Best Ai Document Workflow Platforms](/blog/best-ai-document-workflow-platforms.md)
- [Best Document Ai Platforms](/blog/best-document-ai-platforms.md)
- [Best Document Classification Platforms](/blog/best-document-classification-platforms.md)
- [Best document parser for RAG: LlamaParse vs. Unstructured vs. Reducto vs. Nutrient](/blog/best-document-parser-llamaparse-unstructured-reducto.md)
- [Best Document Parsing Apis](/blog/best-document-parsing-apis.md)
- [Best Document Viewers](/blog/best-document-viewers.md)
- [Best Multilingual Ocr Software](/blog/best-multilingual-ocr-software.md)
- [Best Secure Document Collaboration Platforms](/blog/best-secure-document-collaboration-platforms.md)
- [Bpm Guide](/blog/bpm-guide.md)
- [Bpm Tools](/blog/bpm-tools.md)
- [Build Vs Buy Document Extraction](/blog/build-vs-buy-document-extraction.md)
- [Business Automation](/blog/business-automation.md)
- [Capex Vs Opex](/blog/capex-vs-opex.md)
- [The CEO’s AI playbook: Why decision architecture beats model selection](/blog/ceo-ai-playbook-decision-architecture.md)
- [1. Extract and chunk the PDF.](/blog/chat-with-pdf.md)
- [Complete Guide To Pdfjs](/blog/complete-guide-to-pdfjs.md)
- [Construction Document Data Extraction](/blog/construction-document-data-extraction.md)
- [Convert One Drive Files To Pdf In Sharepoint](/blog/convert-one-drive-files-to-pdf-in-sharepoint.md)
- [Create And Edit Pdfs In Flutter](/blog/create-and-edit-pdfs-in-flutter.md)
- [Create Pdfs With React](/blog/create-pdfs-with-react.md)
- [Creating A Document Scanner With Ocr In Python](/blog/creating-a-document-scanner-with-ocr-in-python.md)
- [Creating And Filling Pdf Forms Programmatically In Javascript](/blog/creating-and-filling-pdf-forms-programmatically-in-javascript.md)
- [The CTO’s AI playbook: Why accountability architecture beats orchestration](/blog/cto-ai-playbook-accountability-architecture.md)
- [Digital Signatures](/blog/digital-signatures.md)
- [Digital Workflow Automation](/blog/digital-workflow-automation.md)
- [Document Ai Vs Ocr](/blog/document-ai-vs-ocr.md)
- [Document Extraction Confidence Scores](/blog/document-extraction-confidence-scores.md)
- [Document Viewer](/blog/document-viewer.md)
- [Document Watermarking](/blog/document-watermarking.md)
- [Emerging threats: Your logging system may be an agentic threat vector](/blog/emerging-threats-your-logging-system.md)
- [Extend Alternatives](/blog/extend-alternatives.md)
- [Extract Patient Data On Premises](/blog/extract-patient-data-on-premises.md)
- [app.py](/blog/extract-text-from-pdf-using-python.md)
- [Fillable Pdf](/blog/fillable-pdf.md)
- [How To Add Digital Signature To Pdf Using React](/blog/how-to-add-digital-signature-to-pdf-using-react.md)
- [How To Build A Dotnet Maui Pdf Viewer](/blog/how-to-build-a-dotnet-maui-pdf-viewer.md)
- [How To Build A Flutter Pdf Viewer](/blog/how-to-build-a-flutter-pdf-viewer.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer-with-pdfjs.md)
- [or](/blog/how-to-build-a-javascript-pdf-viewer.md)
- [or](/blog/how-to-build-a-nextjs-pdf-viewer.md)
- [How To Build A Powerpoint Viewer Using Javascript](/blog/how-to-build-a-powerpoint-viewer-using-javascript.md)
- [Using Yarn](/blog/how-to-build-a-react-excel-viewer.md)
- [How To Build A React Native Pdf Viewer](/blog/how-to-build-a-react-native-pdf-viewer.md)
- [How To Build A React Powerpoint Viewer](/blog/how-to-build-a-react-powerpoint-viewer.md)
- [How To Build A Reactjs File Viewer](/blog/how-to-build-a-reactjs-file-viewer.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer-with-react-pdf.md)
- [or](/blog/how-to-build-a-reactjs-pdf-viewer.md)
- [How To Build A Reactjs Viewer With Pdfjs](/blog/how-to-build-a-reactjs-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer With Pdfjs](/blog/how-to-build-a-vuejs-pdf-viewer-with-pdfjs.md)
- [How To Build A Vuejs Pdf Viewer](/blog/how-to-build-a-vuejs-pdf-viewer.md)
- [How To Build An Android Pdf Viewer](/blog/how-to-build-an-android-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Ng2 Pdf Viewer](/blog/how-to-build-an-angular-pdf-viewer-with-ng2-pdf-viewer.md)
- [How To Build An Angular Pdf Viewer With Pdfjs](/blog/how-to-build-an-angular-pdf-viewer-with-pdfjs.md)
- [How To Convert Docx To Pdf Using Javascript](/blog/how-to-convert-docx-to-pdf-using-javascript.md)
- [How To Convert Docx To Pdf Using Python](/blog/how-to-convert-docx-to-pdf-using-python.md)
- [How To Convert Html To Pdf Using Html2pdf](/blog/how-to-convert-html-to-pdf-using-html2pdf.md)
- [or](/blog/how-to-convert-html-to-pdf-using-react.md)
- [How To Convert Html To Pdf Using Wkhtmltopdf And Csharp](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-csharp.md)
- [or](/blog/how-to-convert-html-to-pdf-using-wkhtmltopdf-and-python.md)
- [How To Convert Html To Pptx](/blog/how-to-convert-html-to-pptx.md)
- [Quarterly report](/blog/how-to-convert-pdf-to-markdown-using-python.md)
- [How To Convert Word To Pdf In Nodejs](/blog/how-to-convert-word-to-pdf-in-nodejs.md)
- [or](/blog/how-to-create-a-react-js-signature-pad.md)
- [How To Create Pdfs With React To Pdf](/blog/how-to-create-pdfs-with-react-to-pdf.md)
- [How To Edit Pdfs Using Ios Pdf Library](/blog/how-to-edit-pdfs-using-ios-pdf-library.md)
- [How To Embed A Pdf Viewer In Your Website](/blog/how-to-embed-a-pdf-viewer-in-your-website.md)
- [How To Extract Tables From Pdf And Images](/blog/how-to-extract-tables-from-pdf-and-images.md)
- [How To Generate Pdf From Html With Nodejs](/blog/how-to-generate-pdf-from-html-with-nodejs.md)
- [base_url tells WeasyPrint where to resolve relative asset paths](/blog/how-to-generate-pdf-reports-from-html-in-python.md)
- [How To Merge Pdfs Using Javascript](/blog/how-to-merge-pdfs-using-javascript.md)
- [How To Ocr Pdfs In Linux](/blog/how-to-ocr-pdfs-in-linux.md)
- [How To Print Pdf In Csharp](/blog/how-to-print-pdf-in-csharp.md)
- [How To Programmatically Create And Fill Pdf Form In Angular](/blog/how-to-programmatically-create-and-fill-pdf-form-in-angular.md)
- [Open an image.](/blog/how-to-use-tesseract-ocr-in-python.md)
- [From an HTML string.](/blog/html-in-pdf-format.md)
- [Intelligent Data Extraction](/blog/intelligent-data-extraction.md)
- [Invoice Approval Software](/blog/invoice-approval-software.md)
- [Javascript Document Editor](/blog/javascript-document-editor.md)
- [Javascript Pdf Editors](/blog/javascript-pdf-editors.md)
- [Javascript Pdf Libraries](/blog/javascript-pdf-libraries.md)
- [Langextract Vs Llamaindex Extraction Comparison](/blog/langextract-vs-llamaindex-extraction-comparison.md)
- [Linearized Pdf](/blog/linearized-pdf.md)
- [Uses OpenAI by default — set OPENAI_API_KEY.](/blog/llamaindex-vs-langchain-rag.md)
- [Llamaindex Workflows Vs Langgraph](/blog/llamaindex-workflows-vs-langgraph.md)
- [Llamaparse Alternatives](/blog/llamaparse-alternatives.md)
- [Low Code No Code Document Integrations](/blog/low-code-no-code-document-integrations.md)
- [Material Requisition](/blog/material-requisition.md)
- [or](/blog/merge-pdfs.md)
- [Swift Package Manager](/blog/mobile-pdf-sdk.md)
- [`elements` come from your document parser — each has a type and content.](/blog/multimodal-rag.md)
- [Nutrient Flutter 6 Bindings Api](/blog/nutrient-flutter-6-bindings-api.md)
- [Nutrient Flutter Bindings Architecture](/blog/nutrient-flutter-bindings-architecture.md)
- [Nutrient Vs Conga Composer](/blog/nutrient-vs-conga-composer.md)
- [Online Document Viewer](/blog/online-document-viewer.md)
- [Open Pdf In Your Web App](/blog/open-pdf-in-your-web-app.md)
- [PDF accessibility for developers: Meeting WCAG 2.2, Section 508, and PDF/UA with an SDK](/blog/pdf-accessibility.md)
- [Extract data from PDF files: A developer guide to structured data from PDFs and scans](/blog/pdf-data-extraction-developer-guide.md)
- [Pdf Extraction Benchmark Opendataloader Bench](/blog/pdf-extraction-benchmark-opendataloader-bench.md)
- [Pdf Extraction Document Case Studies](/blog/pdf-extraction-document-case-studies.md)
- [Pdf Page Labels](/blog/pdf-page-labels.md)
- [Pdf Sdk Compliance Security Checklist](/blog/pdf-sdk-compliance-security-checklist.md)
- [Pdf Sdk Performance Benchmark](/blog/pdf-sdk-performance-benchmark.md)
- [Pdf Ua Compliance Guide](/blog/pdf-ua-compliance-guide.md)
- [Pdf Ua Validation](/blog/pdf-ua-validation.md)
- [Pdfjs Accessibility Structtree Printing](/blog/pdfjs-accessibility-structtree-printing.md)
- [Pdfjs Advanced Loading Streaming Workers](/blog/pdfjs-advanced-loading-streaming-workers.md)
- [Pdfjs Annotation Editor Layer](/blog/pdfjs-annotation-editor-layer.md)
- [Pdfjs Area Annotations Canvas Capture](/blog/pdfjs-area-annotations-canvas-capture.md)
- [Pdfjs Coordinate Systems Pdf To Screen](/blog/pdfjs-coordinate-systems-pdf-to-screen.md)
- [Pdfjs Document Outline Bookmarks Metadata](/blog/pdfjs-document-outline-bookmarks-metadata.md)
- [Pdfjs Eventbus Guide](/blog/pdfjs-eventbus-guide.md)
- [macOS](/blog/pdfjs-file-format-conversion-to-pdf.md)
- [macOS](/blog/pdfjs-generating-pdf-thumbnails-pdf2pic.md)
- [Pdfjs Limitations Commercial Upgrade](/blog/pdfjs-limitations-commercial-upgrade.md)
- [Pdfjs Native Annotation Layer Forms](/blog/pdfjs-native-annotation-layer-forms.md)
- [Pdfjs Navigation Zoom Rotation](/blog/pdfjs-navigation-zoom-rotation.md)
- [Pdfjs Pdf Page Manipulation Pdf Lib](/blog/pdfjs-pdf-page-manipulation-pdf-lib.md)
- [Pdfjs React Viewer Setup](/blog/pdfjs-react-viewer-setup.md)
- [Pdfjs Rendering Overlays React Portals](/blog/pdfjs-rendering-overlays-react-portals.md)
- [Pdfjs Server Side Text Extraction](/blog/pdfjs-server-side-text-extraction.md)
- [Pdfjs Sticky Note Annotations](/blog/pdfjs-sticky-note-annotations.md)
- [Pdfjs Text Highlight Annotations](/blog/pdfjs-text-highlight-annotations.md)
- [Pdfjs Text Search Pdffindcontroller](/blog/pdfjs-text-search-pdffindcontroller.md)
- [Pdfjs Thumbnail Sidebar](/blog/pdfjs-thumbnail-sidebar.md)
- [People Process Tools](/blog/people-process-tools.md)
- [Process Flows](/blog/process-flows.md)
- [React Native Pdf Annotation](/blog/react-native-pdf-annotation.md)
- [React Pdf Annotation Layer Forms](/blog/react-pdf-annotation-layer-forms.md)
- [React Pdf Custom Rendering Hooks](/blog/react-pdf-custom-rendering-hooks.md)
- [Using Yarn](/blog/react-pdf-editor.md)
- [React Pdf Loading States Errors Passwords](/blog/react-pdf-loading-states-errors-passwords.md)
- [React Pdf Non Latin Fonts Special Pdfs](/blog/react-pdf-non-latin-fonts-special-pdfs.md)
- [React Pdf Outline Table Of Contents](/blog/react-pdf-outline-table-of-contents.md)
- [React Pdf Performance Optimization](/blog/react-pdf-performance-optimization.md)
- [React Pdf Setup Basic Rendering](/blog/react-pdf-setup-basic-rendering.md)
- [React Pdf Text Layer Custom Renderer](/blog/react-pdf-text-layer-custom-renderer.md)
- [React Pdf Thumbnails Page Navigation](/blog/react-pdf-thumbnails-page-navigation.md)
- [Reducto Alternatives](/blog/reducto-alternatives.md)
- [Requisition System](/blog/requisition-system.md)
- [labels.py](/blog/route-documents-automatically-classify-api.md)
- [or](/blog/sample-blog-updated.md)
- [Sdk Product Updates Q2 2026](/blog/sdk-product-updates-q2-2026.md)
- [System Of Record Vs Source Of Truth](/blog/system-of-record-vs-source-of-truth.md)
- [Add DWS MCP Server to your Claude Code project.](/blog/teaching-llms-to-read-pdfs.md)
- [Open an image file.](/blog/tesseract-python-guide.md)
- [The Six Best Pdf Generator Apis](/blog/the-six-best-pdf-generator-apis.md)
- [Define the HTML part of the document.](/blog/top-10-ways-to-generate-pdfs-in-python.md)
- [Top 5 Javascript Pdf Viewers](/blog/top-5-javascript-pdf-viewers.md)
- [or](/blog/top-js-pdf-libraries.md)
- [Convert an HTML file to PDF.](/blog/top-ten-ways-to-convert-html-to-pdf.md)
- [Vector Pdf](/blog/vector-pdf.md)
- [Wcag2 Accessibility Requirements Documents](/blog/wcag2-accessibility-requirements-documents.md)
- [Web Sdk Is Now Headless](/blog/web-sdk-is-now-headless.md)
- [What Are Annotations](/blog/what-are-annotations.md)
- [What Is A Vpat](/blog/what-is-a-vpat.md)
- [What Is Business Logic](/blog/what-is-business-logic.md)
- [What Is Document Processing](/blog/what-is-document-processing.md)
- [What Is Intelligent Document Processing](/blog/what-is-intelligent-document-processing.md)
- [What Is Ocr Invoice Processing](/blog/what-is-ocr-invoice-processing.md)
- [What Is Pdf Ua](/blog/what-is-pdf-ua.md)
- [Why Pdfium Is A Trusted Platform For Pdf Rendering](/blog/why-pdfium-is-a-trusted-platform-for-pdf-rendering.md)
- [Why Your Ai Agent Hallucinates Pdf Table Data](/blog/why-your-ai-agent-hallucinates-pdf-table-data.md)

