Skip to content

Stop shipping a browser to print an invoice.

Turn the HTML templates you already have into PDFs with one function call inside Node.js. No Chromium, no Puppeteer.

The top of an English invoice PDF rendered by vellora 0.3.0 from the Invoices guide example, with the seller name, invoice number, bill-to block and the first line items
sh
npm install vellora

Runs on macOS (Apple Silicon and Intel) and on glibc-based Linux, x64 and arm64: Debian or Ubuntu images such as node:22-slim. No Windows or Alpine/musl builds. Node 20 or newer. MIT licensed · npm · GitHub

The same job, minus the browser ​

js
import { renderPdf } from "vellora";

// HTML from your own templates. Page size comes from CSS: @page { size: A4 }
export async function toPdf(html) {
  return renderPdf(html); // a PDF as a Uint8Array
}
js
import puppeteer from "puppeteer";

// One browser, launched at startup and shared by every request
const browser = await puppeteer.launch();

export async function toPdf(html) {
  const page = await browser.newPage();
  try {
    await page.setContent(html, { waitUntil: "load" });
    return await page.pdf({ format: "A4", printBackground: true });
  } finally {
    await page.close();
  }
}
And the Dockerfile
dockerfile
FROM node:22-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci   # vellora's renderer comes prebuilt; nothing else to install
COPY . .
CMD ["node", "server.js"]
dockerfile
FROM node:22-slim
# The system libraries and fonts Chrome needs
RUN apt-get update && apt-get install -y \
    libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
    libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
    libasound2 libpango-1.0-0 libcairo2 fonts-liberation \
  && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package*.json ./
RUN npm ci   # also downloads a Chrome build
COPY . .
# ...plus --no-sandbox if the container runs as root
CMD ["node", "server.js"]

Nothing to launch, keep alive or close. vellora also has built-in templating, so you can pass data as a second argument instead of building the HTML yourself. One thing to know when you switch: HTML you already rendered elsewhere should be passed with { template: false }, so a literal {{ or {% in it stays as text.

Sound familiar? ​

  • Chrome won’t start in the Docker image
  • Missing libnss3 on the server
  • --no-sandbox in production
  • Zombie Chrome processes
  • Fonts change between laptop and server

Puppeteer’s own troubleshooting guide says in its Docker section: “Getting headless Chrome up and running in Docker can be tricky.” With vellora there is no Chrome to install or start. npm install downloads the renderer prebuilt for your platform, and it runs inside your Node process. We checked that in the node:22-slim image: install, render, and a 37 MB node_modules.

Same HTML, side by side ​

Chrome: the top of page 1 of the open-source Sparksuite invoice template printed to PDFvellora: the top of page 1 of the open-source Sparksuite invoice template printed to PDF
Drag the line. If you can’t tell where Chrome ends and vellora starts, that’s the point. Same HTML file; left is Chrome, right is vellora 0.3.0.

On 8 of the 9 documents we test (invoices, receipts, bank slips, a letter, long Portuguese text and two popular open-source templates), every word lands within 1 mm of where Chrome puts it. On the ninth, 97.2% of words do. That is our own measurement against Chrome for Testing 155, so treat it as a guide and check your own templates. See all nine side by side →

  • 19 msper warm render3-page invoice, warm median
  • 118 MBmemory, 8 renders at onceNo Chrome processes: this is the whole Node process
  • 40 MBtotal install sizeno browser to download; a Chromium 130 binary alone is 412 MB

vellora 0.3.0, one benchmark run on a 4-core GitHub Actions Linux runner.

What you can print with it ​

Each picture is page 1 of a real vellora 0.3.0 PDF. See the code for each →

Will it work for your template? ​

vellora is built for documents whose HTML you control, not for printing arbitrary web pages.

Works

  • Works:CSS must be in a style tag or style attribute (vellora fetches nothing from the network)
  • Works:Tables that run over several pages, with the header row repeated
  • Works:Page X of Y in page headers and footers
  • Works:Your own fonts and images, passed in as bytes
  • Works:PDF/A-2b for archiving

Works with limits

  • Works with limits:Flexbox: works for common layouts; tables are the safer choice
  • Works with limits:Floats and multi-column text, with documented edge cases
  • Works with limits:Header and footer text only in the top-center and bottom-center slots

Not supported

  • Not supported:JavaScript: the page is never run
  • Not supported:CSS grid, transforms, animations and filters
  • Not supported:External stylesheets and remote images or fonts
  • Not supported:Windows and Alpine Linux

Most things outside that list stop the render with the line and column, instead of handing you a broken PDF. That includes <link> stylesheets and @import: vellora never loads them, so put your CSS in a <style> tag.

text
VelloraUnsupportedError: Unsupported construct "css:transform" at line 1, column 11: this CSS feature is outside vellora's document subset; run `vellora fix`

The full list lives on Will my template work?

FAQ ​

Does vellora need Chrome or Puppeteer? ​

No. vellora installs from npm with its renderer already built for your platform, and it runs inside your Node process. There is no browser to download, launch or keep running. If one template truly needs Chrome’s output, you can opt that template into an optional Chromium engine; nothing else changes.

Which platforms does it run on? ​

macOS on Apple Silicon and Intel, and Linux on x64 and arm64 with glibc, which covers Debian and Ubuntu images such as node:22-slim. There are no Windows or Alpine/musl builds. It needs Node 20 or newer.

Will my template work? ​

If it is a document you control, such as an invoice, receipt, statement or letter, there is a good chance. Put your CSS in a <style> tag, pass images and fonts as bytes, and avoid CSS grid, transforms and animations. Anything unsupported fails with the line and column instead of printing something wrong.

How close is the output to Chrome? ​

Very close on the documents we test: on 8 of 9, every word is within 1 mm of Chrome’s position. See all nine →

Is it production-ready? ​

It is young. The current release is 0.3.2, and until 1.0 the API can still change between minor versions, so pin the version you test. Every change is listed in the CHANGELOG, and bugs go to GitHub issues. The same input always gives the same PDF bytes, so snapshot tests are a cheap way to catch surprises when you upgrade. To try it on a template of your own in a few minutes, start with your first PDF, then compare it with the PDF you make today.

Is it free, and is it safe with customer data? ​

It is free and MIT licensed. Rendering happens inside your own process and vellora makes no network requests: images and fonts come in as bytes you pass. There is no browser to sandbox or patch, and <script> tags are rejected before anything is rendered.


Ready to try it? Your first PDF in three steps →