FAQ
How is this different from HTML to PDF with Puppeteer or Playwright? Those print a web page with a headless Chrome, so any CSS works, but you ship and run a browser, and each PDF is a browser round trip. Inkoxide has no browser: a native library that lays out documents in milliseconds, with print features built in, such as repeating table headers, page numbers and PDF/A.
Does it run in the browser?
Yes: import from @inkoxide/render/browser, and install @inkoxide/wasm, the engine as
WebAssembly. See In the browser.
The install worked, but the engine does not load.
The engine ships as one package per platform: macOS (arm64, x64), Linux x64 (glibc and musl),
Linux arm64 (glibc) and Windows x64. npm installs the right one as an optional dependency, so it
is missing if you install with --omit=optional, or copy node_modules from one platform to
another (from macOS into a Linux Docker image, say). Run npm install on the platform that runs
the code.
Every render fails with "No fonts found … Set INKOXIDE_ASSETS_DIR".
The bundled fonts and hyphenation patterns are files in the package's assets/ folder
(node_modules/@inkoxide/render/assets), found next to its code. When a bundler (esbuild,
webpack) moves the code into one file, for a serverless function say, they are no longer there.
Copy that folder into the deployment and point the INKOXIDE_ASSETS_DIR environment variable at
the copy: a folder with fonts/ (the bundled .ttf and .otf files) and hyphenation/
(hyph-en-us.tex). Without the hyphenation patterns, text still renders with the engine's
built-in ones, and engineStatus().warnings says so.
Do I need Tailwind installed?
Only to style with className: then npm install @inkoxide/tailwind. It brings the Tailwind
compiler, so there is no config and no CSS build. Without it, style with style.
Can I use my own components and hooks?
Yes. A document is ordinary React: components, props, context, .map and conditionals all work,
as do hooks that compute during render (useMemo, useContext). The PDF is built from that
first render, so state updates and effects do not change it, and nothing can wait for data:
a component that suspends (use() of a promise, a lazy component, inside <Suspense> or
not) fails the render with TEMPLATE_ERROR. Load the data first and pass it in as props.
How much room does a page number take?
Its own digits. In a header, a footer or a fixed element, the text is laid out again on every
page, so <Text className="text-right">Page <PageNumber /> of <TotalPages /></Text> ends at
the footer's padding on every page. In the page body, the text is laid out with the numbers of
the page it lands on, and the page breaks are worked out again if those numbers move it. The
exception is text the body repeats on several pages, a table's header rows: it is sized for a
three-digit number, each page draws its own digits in that room, and a number of four digits
or more can overflow it.
Is the output the same on every machine?
Yes, for the same input, fonts and options, as long as you leave out creationDate.
Synced from packages/render/GUIDE.md in the inkoxide repository.