boxpdf

Open-source PDF extraction for JavaScript runtimes

Read PDFs without loading them.
Render them without losing the page.

@boxpdf/reader turns PDFs into a structured model and pixel-accurate HTML using only the bytes each page needs. It is fast, bounded-memory, and built for Node, edge functions, and the browser—with no WASM, native binary, or headless browser.

Low-memory extraction Edge-function compatible Fast first page Pixel-accurate visual HTML Visual context for LLMs
import { httpSource, openPdf } from "@boxpdf/reader";
import { pageToHtml } from "@boxpdf/html-writer";

const pdf = await openPdf(httpSource(url), {
  maxBytes: 2 * 1024 * 1024,
  maxObjectCacheBytes: 2 * 1024 * 1024
});

const page = await pdf.getPage(0);
viewer.innerHTML = await pageToHtml(page, { profile: "visual" });

Why @boxpdf/reader

A PDF extractor that understands the page.

Most PDF tools choose between pulling out text and drawing pixels. boxpdf keeps the visual evidence—geometry, fonts, vectors, images, and paint order—while exposing it as a JavaScript model and ordinary HTML.

Low memory by design

Fetch and retain only the ranges and objects needed for the current page. Cache limits stay fixed even when the source document grows to thousands of pages.

Made for edge functions

Pure JavaScript with random-access HTTP, Blob, memory, and custom data sources. No filesystem assumption, WASM module, native addon, or browser process.

Fast where users feel it

Open the cross-reference data and go directly to page one—or page 10,000. Work begins before the entire file has crossed the network.

Accurate when rendered

Visual HTML preserves page coordinates, embedded fonts, images, SVG paths, opacity, rotation, and paint order. PDFium raster fixtures gate the result.

Let LLMs look at the PDF, not merely read its text

Render the reconstructed HTML in a browser and give a multimodal model the page as it was designed: columns, labels, callouts, diagrams, handwriting, and spatial relationships intact. Pair that view with the extracted model for grounded document workflows.

Live comparison

The PDF on the left. Reconstructed HTML on the right.

Choose a real document—including a 100 MiB, 1,000-page PDF. Both panes receive the same public R2 URL: the left is a scrollable native PDF viewer; the right extracts and renders page one with @boxpdf/reader and @boxpdf/html-writer. The metrics describe the right-hand conversion, measured live in your browser.

Browser PDF viewercomplete · scrollable
boxpdf visual HTMLpage 1 only · DOM + SVG
open
first page → HTML
bytes transferred
range requests
peak reader cache

Loading the live demo…

Large documents feel small

1,000 pages. First page in milliseconds.

Opening a document does not decode every page. The reader resolves the cross-reference data, fetches page one, and leaves the remaining 999 pages on the source until requested.

openPdf()~1–6 ms
first page extracted~3–20 ms
pages 2–1,000not loaded yet

Local Node 24 benchmark, seven warm/cold runs, synthetic 1,000-page PDF. Hardware and document complexity vary.

10,000jump directly to a page

The memory gate seeks directly to page 10,000 with compact xrefs while retaining bounded caches.

Measured process memory

Input size grows 10×. Reader memory stays flat.

Same logical PDF, isolated Node 24 processes. PDF.js and unpdf use their complete-Uint8Array APIs; @boxpdf/reader uses a random-access source.

140 KiBsource data read from a 100 MiB input
77 KiBArrayBuffer growth
3.64 MiBRSS growth at both 10 and 100 MiB

Peak RSS includes a ~46 MiB Node baseline. Results vary by OS and dependency version. Run the benchmark yourself.

One extraction, two output intents

visual

Display presentation

Absolute positioning, original geometry, SVG vectors, embedded font programs, and page paint order. Use it for viewers, previews, and PDF-to-HTML conversion.

semantic

Document flow

Get clean reading order, nested sections, lists, cards, addresses, financial summaries, and tables that continue across pages. See why the page layout matters →

Install the reader and HTML writer

npm install @boxpdf/reader @boxpdf/html-writer

Random-access sources

openPdf(httpSource(url))
openPdf(blobSource(file))
openPdf(memorySource(bytes))

HTTP range requests, browser files, in-memory bytes, or your own size + read(offset, length) source.

Stream HTML pages

for (let i = 0; i < await pdf.getPageCount(); i++) {
  await write(pageToHtml(await pdf.getPage(i), {
    profile: "visual"
  }));
}

Consume a page at a time without retaining the original PDF or completed HTML output.