HTML to PDF in JavaScript: Five libraries compared (2026)

Table of contents

    This guide covers five approaches to HTML-to-PDF conversion in JavaScript — client-side, server-side, and managed API — with code examples, CSS support notes, and a decision guide.
    HTML to PDF in JavaScript: Five libraries compared (2026)
    TL;DR
    • 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 — Best for quick client-side exports. Zero server setup, but limited CSS support and image-based output.
    • jsPDF — Best for building PDFs programmatically from data. Selectable text, precise layout control, no HTML input.
    • pdfmake — Best for structured, data-driven documents. Define layouts in JSON; selectable text, no HTML input.
    • Playwright — Best for pixel-perfect CSS rendering server-side. Full CSS3 support but requires Node.js infrastructure.

    How to convert HTML to PDF in JavaScript

    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 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().

    // Include via CDN: https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.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 are the fastest to set up. For full CSS3 support — including flexbox, grid, and custom fonts — use 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.

    Five ways to convert HTML to PDF in JavaScript

    There are five main approaches, each with different tradeoffs:

    1. HTML-to-image libraries (html2pdf.js) — Quick setup, converts HTML to canvas, then PDF
    2. Programmatic builders (jsPDF, pdfmake) — Build PDFs from data, selectable text
    3. Server-side automation (Playwright) — High fidelity, full CSS support
    4. Document platforms (Nutrient) — SDKs and APIs with viewing, signing, and more
    5. Custom solutions — Full control but significant development overhead

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

    Open source vs. commercial HTML-to-PDF libraries

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

    ConsiderationOpen sourceNutrient
    CostFreeSubscription after free tier
    SupportCommunity forumsDedicated support, SLAs
    CSS supportVaries by libraryFull CSS3
    InfrastructureSelf-managedManaged or self-hosted
    ComplianceDIYSOC 2, encryption included

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

    Nutrient

    Nutrient 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:

    Terminal window
    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 — Cloud-hosted API for serverless workflows. Supports PDF generation, conversion, OCR, and batch processing.
    // 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 — Chrome-based rendering for local files and live URLs.
    • Mobile SDKs (Android and iOS) — Generate PDFs from HTML on-device.
    • No-code (Zapier 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 automatically
    • Chained operations — Merge, watermark, and manipulate pages in a single API call
    • Cross-platform consistency — Same rendering engine across web, iOS, Android, and server
    • 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(opens in a new tab) library converts HTML pages to PDFs in the browser. It uses html2canvas(opens in a new tab) and jsPDF(opens in a new tab) 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 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
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
    <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:

    <!DOCTYPE html>
    <html>
    10 collapsed lines
    <head>
    <!-- html2pdf CDN link -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.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:

    Terminal window
    npx serve -l 4111 .

    Then open http://localhost:4111(opens in a new tab) in your browser to interact with the PDF download button.

    html2pdf.js converting an HTML element to a downloadable PDF in the browser

    Open PDFs in a 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:

    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(opens in a new tab) 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

    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(opens in a new tab) 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

    Terminal window
    npm install jspdf

    Or via CDN:

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

    Basic example with text and images

    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 positioningQuick prototypes with HTML
    Adding images, shapes, tables programmaticallyCSS styling is important
    Smaller file sizes matterSimplicity 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(opens in a new tab) 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(opens in a new tab).

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

    Installation

    Add via CDN:

    <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:

    Terminal window
    npm install pdfmake

    Generate PDFs 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.

    <!DOCTYPE html>
    <html>
    12 collapsed lines
    <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: {
    3 collapsed lines
    header: { fontSize: 18, bold: true },
    },
    };
    pdfMake.createPdf(docDefinition).download("invoice.pdf");
    }
    </script>
    </body>
    </html>

    You can test the example by running:

    Terminal window
    npx serve -l 4111 .

    pdfmake generating a PDF from a JavaScript object definition — invoice table example

    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.

    Playwright

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

    Playwright(opens in a new tab) controls headless Chrome, Firefox, and WebKit browsers to convert webpages into PDFs. Unlike html2pdf, Playwright runs on your server and generates PDFs server-side.

    Installation

    Create a new project and install Playwright(opens in a new tab):

    Terminal window
    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:

    Terminal window
    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:

    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:

    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

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

    Featurehtml2pdf.jsjsPDFpdfmakePlaywrightNutrient
    Ease of useVery easyEasyModerateModerateEasy
    InstallationSimple (CDN, npm)Simple (CDN, npm)Simple (CDN, npm)npmSDK/API
    Runs in browserCheckCheckCheckCrossCheck
    Text selectionCrossCheckCheckCheckCheck
    CSS/HTML supportLimitedNoneNoneFullFull
    CustomizationLowHighHighHighHigh
    PDF qualityMedium (image-based)High (vector)High (structured)Very high (browser)Very high (Chrome-based)
    Node.js supportCrossCheckCheckCheckCheck
    Best forQuick HTML exportsData-driven PDFsJSON-based layoutsComplex CSS layoutsProduction apps, compliance
    SupportCommunityCommunityCommunityCommunityEnterprise SLA
    Convert HTML to PDF with annotations

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

    Start your free trial 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 caseRecommendedWhy
    Quick prototypehtml2pdf.jsFastest setup, no server needed
    Invoices from datajsPDF or pdfmakeBuild PDFs programmatically, selectable text
    Complex CSS layoutsPlaywrightFull browser rendering, pixel-perfect
    Production appNutrientManaged infrastructure, support, compliance
    Fillable formsNutrientOnly option with form field conversion
    Mobile appsNutrientiOS/Android SDKs available

    Decision flowchart

    1. Need fillable PDF forms? → Nutrient
    2. Running on mobile? → Nutrient (iOS/Android SDKs)
    3. Need full CSS support? → Playwright (self-hosted) or Nutrient (managed)
    4. Building from data, not HTML? → jsPDF or pdfmake
    5. Simple prototype? → html2pdf.js

    Convert HTML to PDF in Node.js

    For server-side HTML-to-PDF conversion in Node.js, you have two main options:

    • Playwright — Launches a headless browser on your server, renders your HTML with full CSS support, and outputs a PDF. Best when you need pixel-perfect rendering and already have server infrastructure.
    • Nutrient Processor API — A managed cloud API that accepts an HTML file and returns a PDF. No browser installation or server maintenance required.

    See the Playwright and Nutrient sections above for full code examples. If you need to generate PDFs from HTML templates at scale, the Nutrient Node.js guide covers batching, headers, footers, and page options in detail.

    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 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 Playwright (server-side) or Nutrient. Both 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 converts HTML form elements into fillable PDF fields, including text inputs, checkboxes, and dropdowns.

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

    Use Playwright or Nutrient’s Processor API. With Playwright, install the package, launch a headless browser session, call page.setContent() with your HTML, and use page.pdf() to generate the file. With Nutrient, send a POST request to the API with your HTML file and receive a PDF in response — no browser infrastructure required. See the Playwright and Nutrient sections above for full code examples.

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

    Nicolas Dular

    Nicolas Dular

    Hulya Masharipov

    Hulya Masharipov

    Technical Writer

    Hulya is a frontend web developer and technical writer who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

    Mahmoud Elsayad

    Mahmoud Elsayad

    Web Senior Software Engineer

    Explore related topics

    Try for free Ready to get started?