eddiesr93/inkoxide

Print-quality PDFs from React components, with a native layout engine in Rust

Rust

4

584 commits

updated Oct 18, 2026

See the code

See what people are saying

SourceMessageScoreDate

react-pdf took over a minute for my 70-page PDF, so I built a renderer with a Rust layout engine (r/reactjs)

I had a 70-page PDF to generate from React in a TanStack Start app, and with react-pdf it took over 60 seconds. So I built @inkoxide/render. It's not a react-pdf clone, though it's meant to feel familiar: Document, Page, View, Text, Image, StyleSheet.create and Font.register work the way you'd…

0

Oct 16, 2026

README

Inkoxide

A PDF layout engine for Node.js, written in Rust.

Typeset documents from React components, or from a JSON document tree.

CI @inkoxide/render on npm inkoxide on npm Licence: MIT

Getting started · Documentation · Packages · Performance · Contributing

The playground: the invoice's title is edited in the source, the PDF renders again in the browser, and Layout shows the boxes the engine laid out, with their sizes in points.
Edit a document in the playground: it renders again in the browser, and Layout shows the boxes the engine laid out.

Invoice PDF: logo, sender and client addresses, a four-row line-item table, subtotal, tax and a highlighted total due.
Invoice
First page of a sales report PDF: three KPI cards, a monthly revenue bar chart and the start of a regional sales table.
Multi-page report
Landscape certificate PDF: a double border, Certificate of Completion, the recipient name, two signature lines and a gold seal.
Certificate
Business letter PDF: a letterhead, the date and address block, four justified paragraphs and a signature.
Business letter
Résumé PDF: a dark sidebar with initials, contact details and skill tags, and a main column with profile, experience and education.
Résumé
Membership application PDF with text fields, a plan select, two radio buttons and two consent checkboxes.
Fillable form

Inkoxide does the typesetting of a PDF: text shaping, line breaking, layout, pagination and PDF output, in native code. Documents are written as React components with @inkoxide/render, styled with Tailwind classes or style objects, or built as JSON by any program and rendered with inkoxide. A one-page invoice renders in about a millisecond; a 98-page report with 40 tables in about 130 ms.

Features

  • Typesetting. OpenType shaping, justification, hyphenation with TeX patterns, orphans and widows, font fallback and right-to-left text.
  • Layout. Flexbox and CSS grid, absolute positioning, transforms, borders and radii.
  • Pagination. Tables that split across pages with repeating header rows, headers and footers, page numbers, keep-together blocks and page breaks.
  • Content. JPEG, PNG, GIF, WebP and SVG images, vector drawing, links, bookmarks, fillable form fields and text notes.
  • Output. Font subsetting (TrueType and CFF), PDF/A-1b, 2b and 3b, AES-256 encryption with passwords and permissions, parallel rendering across pages, and byte-identical output for identical input.
  • Validation. Every document is checked against a JSON Schema, with the path of each error. In React, errors carry the component stack.

Getting started

With React

npm install @inkoxide/render

To style with Tailwind classes (className), as the example does, add:

npm install @inkoxide/tailwind

Save this as receipt.tsx:

/** @jsxRuntime automatic */
import { Document, Page, renderToFile, Text, View } from '@inkoxide/render';

function Receipt({ customer, lines }: { customer: string; lines: { name: string; amount: number }[] }) {
  const total = lines.reduce((sum, l) => sum + l.amount, 0);
  return (
    <Document title={`Receipt for ${customer}`}>
      <Page size="A6" className="p-8 text-xs text-gray-900">
        <Text className="text-lg font-bold">Receipt</Text>
        <Text className="mb-4 text-gray-500">for {customer}</Text>
        {lines.map((l) => (
          <View key={l.name} className="flex-row justify-between border-b border-gray-200 py-1.5">
            <Text>{l.name}</Text>
            <Text>${l.amount.toFixed(2)}</Text>
          </View>
        ))}
        <View className="mt-3 flex-row justify-between font-bold">
          <Text>Total</Text>
          <Text>${total.toFixed(2)}</Text>
        </View>
      </Page>
    </Document>
  );
}

renderToFile(
  <Receipt
    customer="Ada Lovelace"
    lines={[
      { name: 'Espresso', amount: 3.2 },
      { name: 'Almond croissant', amount: 4.5 },
      { name: 'Sparkling water', amount: 2 },
    ]}
  />,
  'receipt.pdf',
);
The resulting receipt.pdf: an A6 page with 'Receipt for Ada Lovelace', three items with prices and a bold total of $9.70.

Run it with npx tsx receipt.tsx. The fonts (Inter, Noto Sans, Roboto Mono) ship with the package, and @inkoxide/tailwind brings the Tailwind compiler. The six documents above, with their code, are in examples/ and in the @inkoxide/render README.

With JSON

import { readFileSync, writeFileSync } from 'node:fs';
import { registerFont, render } from 'inkoxide';

registerFont('Inter-Bold', readFileSync('Inter-Bold.ttf'));

const doc = {
  version: 1,
  fonts: [{ id: 'Inter-Bold', family: 'Inter', weight: 700 }],
  pages: [
    {
      size: { width: 595.28, height: 841.89 },
      margin: { top: 56, right: 56, bottom: 56, left: 56 },
      children: [{ type: 'text', runs: [{ text: 'Hello, PDF', font: 'Inter-Bold', size: 28 }] }],
    },
  ],
};

writeFileSync('hello.pdf', await render(JSON.stringify(doc)));

The document tree is a fully resolved description of boxes, text, images and tables, as JSON or MessagePack. @inkoxide/schema has its TypeScript types and JSON Schema.

Documentation

TopicDocument
Writing documents in ReactGuide: components, styling, fonts, images, pagination, tables, forms, API, errors
Tailwind classesSupported utilities
Using the engine from NodeClient integration: fonts, images, errors, threading, determinism
The engine APIAPI reference: the Rust and Node APIs, options and error codes
The document treeSchema reference and example documents
How it worksArchitecture
Testing and benchmarksTesting and benchmarks

Packages

PackageWhat it is
@inkoxide/renderReact components to PDF: renderToBuffer, renderToFile, Font.register, bundled fonts
@inkoxide/reactThe React components and reconciler that build document trees
@inkoxide/tailwindOptional: Tailwind class strings (className) to engine styles, with the Tailwind v4 compiler
@inkoxide/styleStyle values: CSS lengths, colours and transforms read into engine styles
inkoxideThe engine for Node.js: a JSON or MessagePack document tree in, PDF bytes out
@inkoxide/schemaTypeScript types and JSON Schema of the document tree
inkoxide crateThe engine as a Rust library

Performance

Median render times after warm-up:

DocumentPagesAll coresOne threadMachine
Invoice, from React11.5 ms1.2 msApple M4 Pro, Node 24
Report, from React: 40 sections, 1,000 table rows4341 ms54 msApple M4 Pro, Node 24
Report, from React, in the browser (WebAssembly, in a worker)4356 msApple M4 Pro, Chromium 153
Report, from JSON: 40 tables, header, footer98130 ms277 ms4-vCPU Xeon container

Layout and PDF writing run in parallel across pages, and fonts are parsed once per process. docs/BENCHMARKS.md has the methodology, per-stage profiles and comparisons with other libraries, on the server and in the browser.

Platforms

Prebuilt binaries; npm installs the one for the current platform. Every package needs Node.js 22 or later and is an ES module.

OSArchitectures
macOSarm64, x64
Linuxx64 (glibc 2.35+ and musl), arm64 (glibc 2.35+)
Windowsx64

In browsers (current Chrome, Edge, Firefox and Safari), the engine is WebAssembly (@inkoxide/wasm), and @inkoxide/render/browser renders in a Web Worker; see In the browser.

Contributing

Issues and pull requests are welcome. CONTRIBUTING.md explains how to build the project, run the tests and propose a change, and docs/ARCHITECTURE.md how the code is organised. Please report security issues privately, as described in SECURITY.md. Changes are listed in CHANGELOG.md.

Licence

MIT. The bundled fonts are under the SIL Open Font License (fixtures/fonts/*-OFL.txt).

pdf
react
rust
tailwind
typesetting

eddiesr93/inkoxide

Print-quality PDFs from React components, with a native layout engine in Rust

Rust

4

584 commits

updated Oct 18, 2026

See the code

See what people are saying

SourceMessageScoreDate

react-pdf took over a minute for my 70-page PDF, so I built a renderer with a Rust layout engine (r/reactjs)

I had a 70-page PDF to generate from React in a TanStack Start app, and with react-pdf it took over 60 seconds. So I built @inkoxide/render. It's not a react-pdf clone, though it's meant to feel familiar: Document, Page, View, Text, Image, StyleSheet.create and Font.register work the way you'd…

0

Oct 16, 2026

README

Inkoxide

A PDF layout engine for Node.js, written in Rust.

Typeset documents from React components, or from a JSON document tree.

CI @inkoxide/render on npm inkoxide on npm Licence: MIT

Getting started · Documentation · Packages · Performance · Contributing

The playground: the invoice's title is edited in the source, the PDF renders again in the browser, and Layout shows the boxes the engine laid out, with their sizes in points.
Edit a document in the playground: it renders again in the browser, and Layout shows the boxes the engine laid out.

Invoice PDF: logo, sender and client addresses, a four-row line-item table, subtotal, tax and a highlighted total due.
Invoice
First page of a sales report PDF: three KPI cards, a monthly revenue bar chart and the start of a regional sales table.
Multi-page report
Landscape certificate PDF: a double border, Certificate of Completion, the recipient name, two signature lines and a gold seal.
Certificate
Business letter PDF: a letterhead, the date and address block, four justified paragraphs and a signature.
Business letter
Résumé PDF: a dark sidebar with initials, contact details and skill tags, and a main column with profile, experience and education.
Résumé
Membership application PDF with text fields, a plan select, two radio buttons and two consent checkboxes.
Fillable form

Inkoxide does the typesetting of a PDF: text shaping, line breaking, layout, pagination and PDF output, in native code. Documents are written as React components with @inkoxide/render, styled with Tailwind classes or style objects, or built as JSON by any program and rendered with inkoxide. A one-page invoice renders in about a millisecond; a 98-page report with 40 tables in about 130 ms.

Features

  • Typesetting. OpenType shaping, justification, hyphenation with TeX patterns, orphans and widows, font fallback and right-to-left text.
  • Layout. Flexbox and CSS grid, absolute positioning, transforms, borders and radii.
  • Pagination. Tables that split across pages with repeating header rows, headers and footers, page numbers, keep-together blocks and page breaks.
  • Content. JPEG, PNG, GIF, WebP and SVG images, vector drawing, links, bookmarks, fillable form fields and text notes.
  • Output. Font subsetting (TrueType and CFF), PDF/A-1b, 2b and 3b, AES-256 encryption with passwords and permissions, parallel rendering across pages, and byte-identical output for identical input.
  • Validation. Every document is checked against a JSON Schema, with the path of each error. In React, errors carry the component stack.

Getting started

With React

npm install @inkoxide/render

To style with Tailwind classes (className), as the example does, add:

npm install @inkoxide/tailwind

Save this as receipt.tsx:

/** @jsxRuntime automatic */
import { Document, Page, renderToFile, Text, View } from '@inkoxide/render';

function Receipt({ customer, lines }: { customer: string; lines: { name: string; amount: number }[] }) {
  const total = lines.reduce((sum, l) => sum + l.amount, 0);
  return (
    <Document title={`Receipt for ${customer}`}>
      <Page size="A6" className="p-8 text-xs text-gray-900">
        <Text className="text-lg font-bold">Receipt</Text>
        <Text className="mb-4 text-gray-500">for {customer}</Text>
        {lines.map((l) => (
          <View key={l.name} className="flex-row justify-between border-b border-gray-200 py-1.5">
            <Text>{l.name}</Text>
            <Text>${l.amount.toFixed(2)}</Text>
          </View>
        ))}
        <View className="mt-3 flex-row justify-between font-bold">
          <Text>Total</Text>
          <Text>${total.toFixed(2)}</Text>
        </View>
      </Page>
    </Document>
  );
}

renderToFile(
  <Receipt
    customer="Ada Lovelace"
    lines={[
      { name: 'Espresso', amount: 3.2 },
      { name: 'Almond croissant', amount: 4.5 },
      { name: 'Sparkling water', amount: 2 },
    ]}
  />,
  'receipt.pdf',
);
The resulting receipt.pdf: an A6 page with 'Receipt for Ada Lovelace', three items with prices and a bold total of $9.70.

Run it with npx tsx receipt.tsx. The fonts (Inter, Noto Sans, Roboto Mono) ship with the package, and @inkoxide/tailwind brings the Tailwind compiler. The six documents above, with their code, are in examples/ and in the @inkoxide/render README.

With JSON

import { readFileSync, writeFileSync } from 'node:fs';
import { registerFont, render } from 'inkoxide';

registerFont('Inter-Bold', readFileSync('Inter-Bold.ttf'));

const doc = {
  version: 1,
  fonts: [{ id: 'Inter-Bold', family: 'Inter', weight: 700 }],
  pages: [
    {
      size: { width: 595.28, height: 841.89 },
      margin: { top: 56, right: 56, bottom: 56, left: 56 },
      children: [{ type: 'text', runs: [{ text: 'Hello, PDF', font: 'Inter-Bold', size: 28 }] }],
    },
  ],
};

writeFileSync('hello.pdf', await render(JSON.stringify(doc)));

The document tree is a fully resolved description of boxes, text, images and tables, as JSON or MessagePack. @inkoxide/schema has its TypeScript types and JSON Schema.

Documentation

TopicDocument
Writing documents in ReactGuide: components, styling, fonts, images, pagination, tables, forms, API, errors
Tailwind classesSupported utilities
Using the engine from NodeClient integration: fonts, images, errors, threading, determinism
The engine APIAPI reference: the Rust and Node APIs, options and error codes
The document treeSchema reference and example documents
How it worksArchitecture
Testing and benchmarksTesting and benchmarks

Packages

PackageWhat it is
@inkoxide/renderReact components to PDF: renderToBuffer, renderToFile, Font.register, bundled fonts
@inkoxide/reactThe React components and reconciler that build document trees
@inkoxide/tailwindOptional: Tailwind class strings (className) to engine styles, with the Tailwind v4 compiler
@inkoxide/styleStyle values: CSS lengths, colours and transforms read into engine styles
inkoxideThe engine for Node.js: a JSON or MessagePack document tree in, PDF bytes out
@inkoxide/schemaTypeScript types and JSON Schema of the document tree
inkoxide crateThe engine as a Rust library

Performance

Median render times after warm-up:

DocumentPagesAll coresOne threadMachine
Invoice, from React11.5 ms1.2 msApple M4 Pro, Node 24
Report, from React: 40 sections, 1,000 table rows4341 ms54 msApple M4 Pro, Node 24
Report, from React, in the browser (WebAssembly, in a worker)4356 msApple M4 Pro, Chromium 153
Report, from JSON: 40 tables, header, footer98130 ms277 ms4-vCPU Xeon container

Layout and PDF writing run in parallel across pages, and fonts are parsed once per process. docs/BENCHMARKS.md has the methodology, per-stage profiles and comparisons with other libraries, on the server and in the browser.

Platforms

Prebuilt binaries; npm installs the one for the current platform. Every package needs Node.js 22 or later and is an ES module.

OSArchitectures
macOSarm64, x64
Linuxx64 (glibc 2.35+ and musl), arm64 (glibc 2.35+)
Windowsx64

In browsers (current Chrome, Edge, Firefox and Safari), the engine is WebAssembly (@inkoxide/wasm), and @inkoxide/render/browser renders in a Web Worker; see In the browser.

Contributing

Issues and pull requests are welcome. CONTRIBUTING.md explains how to build the project, run the tests and propose a change, and docs/ARCHITECTURE.md how the code is organised. Please report security issues privately, as described in SECURITY.md. Changes are listed in CHANGELOG.md.

Licence

MIT. The bundled fonts are under the SIL Open Font License (fixtures/fonts/*-OFL.txt).

pdf
react
rust
tailwind
typesetting

Project health

Latest release

0.9.0 · Oct 16, 2026

Releases this year

16