Skip to content

Generate PDFs in Node.js

Fullbleed's optional Node package renders static HTML/CSS into PDFs using a bundled WebAssembly build of the Rust engine. It includes fonts and runs locally; you do not need Python, Rust, or a browser to use the installed package.

To compose documents without HTML strings, use the JavaScript component and JSX workflow, introduced in Node package 0.6.0. Version 0.7.0 also supports static HTML export with or without styles. The pinned HTML/CSS example below remains available.

The quickstart and downloadable invoice use Node package 0.7.1, powered by Fullbleed 2.5.29. The integration and core engine have separate versions.

Version 0.7.1 brings the stable engine's SVG stylesheet and inset-shadow fixes to Node and browser rendering. It retains component/JSX authoring, static HTML export, the shared render queue, optional process isolation and bundled fonts. See the focused paint checks and the server guidance below for queue limits and failure handling.

Use the actual family name for normal/italic CSS selection, or a PostScript name for an explicit face. Review appearance and line breaks when upgrading; the font-family guide explains font selection.

For rendering inside a web page, use the browser entry and starter. For Cloudflare Workers, use the Workers adapter and invoice endpoint introduced in package 0.8.0. This guide's invoice download remains pinned to 0.7.1.

Create your first PDF

With Node.js 22 or newer, install the npm package:

npm install fullbleed

To pin this guide's version, use npm install --save-exact fullbleed@0.7.1. The npm package contains the same verified bytes as the GitHub release.

Save this as invoice.mjs and run node invoice.mjs:

import { writeFile } from 'node:fs/promises';
import { renderPdf } from 'fullbleed';

const result = await renderPdf({
  html: '<h1>Invoice NS-1042</h1><p>Consulting: USD 1,200.00</p>',
  css: '@page { size: A4; margin: 20mm } h1 { color: #175c52 }',
  previewDpi: 96,
});

await writeFile('invoice.pdf', result.pdf);
await writeFile('invoice.png', result.previews[0]);
console.log(`${result.pages} page; engine ${result.engineVersion}`);

Open invoice.pdf or invoice.png. Inter is the default font, and previews come from the finalized PDF. result.pdf is a Node Buffer that you can also return from an HTTP handler or write to your storage.

CommonJS applications can use const { renderPdf } = require('fullbleed'). TypeScript declarations are included for both module styles.

Export a design from the playground

Open the playground, edit an invoice, report, or notice, choose Node.js under Project language, and select Download project. The ZIP contains your current HTML/CSS, font files and licenses, a locked npm dependency, and render.mjs. Extract it and run:

npm ci --ignore-scripts
npm run render

Open output/document.pdf and the previews in output/preview/. Edit input.html or style.css and run the renderer again. These playground exports use Node package 0.5.0 with the same 2.5.22 engine. Their renderer sets defaultStyles: false to preserve font inheritance and CSS layers exactly as entered in the playground.

Start with a designed invoice

Northstar Studio invoice generated by the Node package, with cream paper, serif typography, a green total panel, and orange accents.

Download the Node invoice project Open its generated PDF

Extract the project and run these commands in its directory:

npm ci --ignore-scripts
npm run render

Open output/invoice/invoice.pdf and output/invoice/page-1.png. Edit the customer value in render.mjs or change the HTML/CSS. The script escapes the customer name before inserting it into the document. All example data is fictional. The lockfile pins the npm package version and its integrity hash.

The layout uses the bundled Inter, DM Serif Display, and Bebas Neue families.

Style a report with gradients

Report page with serif headings, large summary figures, gradient chart bars, and a numbered footer.

This retained sample comes from package 0.4.1's three-page report. The chart uses CSS linear-gradient(180deg, #d9bc77, #a9b884); the image above is a native preview of the finalized PDF. Fullbleed 2.5.5 fixes gradient fills that were missing from these PNG previews in earlier releases.

Open the report PDF Explore its HTML and CSS

Bring fonts and image assets

Pass additional font bytes through fonts, and use their actual family names in CSS. Pass images through assets, mapping relative names to Buffer or Uint8Array values. For example, an asset named brand/logo.svg is available as assets/brand/logo.svg in your HTML.

The engine uses an in-memory filesystem for the supplied inputs and does not fetch remote URLs. Data URIs also work for document images. The package API reference includes a complete font and asset example.

Unembedded PDF Standard 14 faces now have bundled substitutes for native PNG previews. The PDF keeps its original font resources, text, and advances; a PDF reader can use a different substitute design. Register your chosen font when the exact branded appearance matters. See the preview comparison and font limits.

Use it in an application

For a working web-app integration, use the Next.js PDF download starter. It includes an App Router handler, designed invoice, bundled-asset configuration and an isolated check of the production standalone server.

By default, rendering runs in a worker, with separate document state for concurrent calls. The promise waits for its worker to exit, including after failure, timeout, or cancellation. Sequential await renderPdf() calls therefore do not overlap worker lifetimes. A timeout or abort starts termination; the promise settles after the worker stops.

Use timeoutMs, maxPages, and an AbortSignal to bound a job. Limit concurrent calls to fit your server's memory; each worker's WebAssembly memory ceiling is 512 MiB. Preview images can use more memory than PDF-only rendering.

Node package 0.4.0 adds a shared render queue with active and waiting limits, deadlines that include queue time, cancellation, and shutdown. The guide includes a runnable six-invoice batch. Use one queue per application process to share the limits across request handlers.

Rendering errors reject with a FullbleedError and a code such as INVALID_INPUT, MISSING_GLYPHS, PAGE_LIMIT, TIMEOUT, or ABORTED. Invalid font data is rejected. Use registered font families: unavailable CSS families can fall back, and the engine's glyph report does not establish visual correctness. Review the final PDF and previews when adapting a design.

Contain render-process failures

On a Node host that permits child processes, use isolation: 'process' to run each request's engine and worker in a fresh process:

const result = await renderPdf({
  html: '<h1>Monthly statement</h1>',
  isolation: 'process',
  timeoutMs: 10_000,
  maxPages: 20,
});

The caller does not compile WebAssembly or create render workers in this mode. The promise waits for process exit and IPC disconnection before settling, even after success. A failed child exit rejects with PROCESS_FAILED; available exitCode and signal fields describe the exit. Timeouts and aborts terminate the child before rejecting. A PDF received before a failed shutdown is not returned as a successful result.

Process startup adds latency and memory use. There is no process pool or automatic retry; keep concurrency bounded. Application startup flags and NODE_OPTIONS are not replayed in the child. This option requires a normal Node executable and is not an operating-system security sandbox. See the process isolation guide for deployment details and the tested failure paths. The Next.js starter uses this mode for its PDF route.

An intermittent process crash on Linux with Node 24.21.0 was observed with fullbleed@0.1.2; issue #7 remains open. Version 0.1.3 fixes worker shutdown timing, but has not established that this native crash is resolved. See the runtime investigation for the observed scope and synthetic diagnostic. A process crash can occur before JavaScript can return an error in worker mode. Process isolation contains tested child failures; it does not establish a fix for that native crash.

This first Node API covers ordinary PDF rendering and previews. Use the Python API or Rust crate for profiles, template overlays, and compiled variable-data workflows. Unknown Node options are rejected. For JavaScript execution or live website behavior, use a browser.

Verification and source

The package checks real document output, failure recovery, ESM/CommonJS, and TypeScript. Its release evidence compares retained native and WebAssembly fixtures and installs the same tarball across Node 22, 24, and 26 on Windows, Linux, and macOS. These are scoped engineering checks, not PDF standards certification.

The public-install verification records the registry tarball hash, audits npm signatures and provenance, and executes the queue lifecycle checks from a fresh registry install. Its queued PDF and PNG match the CI output. The release verification retains 45 native/Wasm comparisons, fourteen queue checks in each of nine installed Node/platform combinations, and 144 checks in each of Chrome, Firefox, and Playwright WebKit, including browser downloads and failure recovery. Thirteen focused engine fixtures also pass independent text, color, and retained Chrome geometry expectations in Node and downloaded browser PDFs. The invoice and report on this page retain the reviewed PDF bytes and preview pixels with engine 2.5.22; the download manifest records their exact hashes.

Package source and API · npm package · Versioned release and evidence · CSS coverage · Examples