Skip to content

Latest commit

 

History

122 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
imprint-pdf - author PDFs as React components, styled with real Tailwind, rendered without Chromium.

License: Apache-2.0 Status: 1.0.0-alpha

Generate PDFs from React components, styled with real Tailwind, without Chromium.

import { pdf, Document, Page } from '@imprint-pdf/react';

const response = await pdf(
  <Document>
    <Page size="A4" className="p-12 font-sans bg-white">
      <h1 className="text-3xl font-bold tracking-tight">Hello, PDF</h1>
      <p className="mt-4 text-base leading-relaxed text-pretty">
        Real Tailwind. Real React. Real typography. No browser.
      </p>
    </Page>
  </Document>,
);

That's it. pdf() returns a Response you can hand to any web framework. Pass { as: 'bytes' } for a Uint8Array, { as: 'stream' } for a ReadableStream.

Why

Making a PDF in a JavaScript app usually means one of two bad deals: boot a 200 MB headless Chromium, or learn a bespoke layout DSL and nudge coordinates by hand.

imprint does neither. You write React and Tailwind - the same code you already write - and get a PDF back. It runs wherever your app runs: Node, Bun, the browser, and edge runtimes like Vercel Edge and Cloudflare Workers.

The trick is that it uses the real tools, not lookalikes: the actual Tailwind compiler, professional text shaping and line-breaking, and a real CSS layout engine with Flexbox and Grid. All Apache-2.0, all in one install. (For the curious, that's Tailwind Oxide, HarfBuzz, Knuth–Plass, and Taffy.)

Features

  • Real Tailwind, not a subset. Your actual p-4 grid grid-cols-12 gap-6 classes run through the real compiler - plugins, arbitrary values, OKLCH colors, and @theme tokens all work. Tailwind v3 and v4, auto-detected.
  • Real React. JSX, components, hooks, and refs, through a custom reconciler. HTML tags (<div>, <h1>, <table>) map to tagged PDF nodes. React 18 and 19.
  • Real typography. Proper shaping for Arabic, Indic, Thai, and CJK, plus kerning, ligatures, and variable fonts - and paragraph and page breaking that respects widows, orphans, and footnotes.
  • One function, everywhere. pdf(<Doc />) returns a Response; { as: 'bytes' } gives a Uint8Array, { as: 'stream' } a ReadableStream. Same call on Node, Bun, the browser, and edge.
  • Edge-ready. Sub-100 ms cold starts on Cloudflare Workers and Vercel Edge - there is no browser to boot.
  • CSS Grid and Flexbox. One real layout pass, not a flexbox-only approximation.
  • Charts as vectors. Recharts, Visx, ECharts, and Observable Plot SVG flow through crisp at any zoom - not a rasterized screenshot.
  • Real form fields. <TextField>, <Checkbox>, <RadioGroup>, and <Signature> become actual PDF AcroForm fields.
  • Print-grade output, opt-in. PDF/X-4 + CMYK + ICC profiles, PDF/UA-1 tagged PDF, PKCS#7 signing, and factur-X / ZUGFeRD.

Documentation

Full documentation lives in docs/. For LLMs / AI agents, see llms.txt (index) and llms-full.txt (single-file context).

  • Overview - what imprint-pdf does and where it fits
  • Quick start - render your first PDF in five minutes
  • Philosophy - why no Chromium, why real Tailwind
  • Concepts - documents, pages, layout, the writer
  • Guides - components, Tailwind, fonts, charts, forms, accessibility
  • Frameworks - Next.js, Vite, Bun, Cloudflare
  • Cookbook - invoices, contracts, reports
  • Reference - every component, every CLI flag

Install

pnpm add @imprint-pdf/react @imprint-pdf/core tailwindcss
pnpm add -D @imprint-pdf/cli
npx imprint init

imprint init detects your framework (Next.js, Vite, generic Node), wires the right plugin into your config, scaffolds an example template + route, and prints the first command to run.

Render

The same pdf() call works in every runtime. Pick yours.

Next.js / Hono / Bun route
// returns a Response with PDF headers
import { pdf } from '@imprint-pdf/next';
export const GET = () => pdf(<Invoice data={data} />);
Cloudflare Worker (edge)
// same pdf(), edge-friendly build
import { pdf } from '@imprint-pdf/react';
import wasm from '@imprint-pdf/react/imprint.wasm';

export default {
  fetch: () => pdf(<Invoice data={} />, { wasm }),
};
Write to disk (Node / Bun)
// { as: 'bytes' } gives back a Uint8Array
import { pdf } from '@imprint-pdf/react';
import { writeFileSync } from 'node:fs';

const bytes = await pdf(<Invoice data={data} />, { as: 'bytes' });
writeFileSync('./invoice.pdf', bytes);

How it works

Source code  →  React reconciler  →  PdfNode IR
                                          │
                                          ▼
                                  Tailwind resolver
                              (Oxide v4 or PostCSS v3)
                                          │
                                          ▼
                            Layout pass  (Taffy WASM)
                          + inline layout (Parley-lite)
                          + Knuth–Plass paragraph breaker
                          + Plass page breaker
                                          │
                                          ▼
                            Text & vector pipeline
                          (HarfBuzz, ICU4X, fontkit, resvg)
                                          │
                                          ▼
                            PDF writer  (pdf-lib v0 → imprint-pdf v1)
                                          │
                                          ▼
                            Response | Uint8Array | ReadableStream

For the full design, see ARCHITECTURE.md.

How it compares

Capability @react-pdf Satori pdfme Forme Chromium SaaS Typst imprint-pdf
Real Tailwind classes ❌ (DSL) subset
React / JSX components part.
Edge runtimes (CF Workers) part. part.
CSS Grid ✅ (Taffy)
HarfBuzz-grade shaping
Knuth–Plass justification Prince ≈
Variable fonts part.
Arabic / Indic / Thai / CJK weak weak weak part.
Vector charts
AcroForms in JSX n/a tmpl.
PDF/X-4 + CMYK part. DocRaptor part.
PDF/UA-1 tagged PDF DocRaptor part.
Sub-100 ms edge cold start n/a part.
Same code: client + edge + server part. n/a

Benchmarks

Measured with @imprint-pdf/bench on an Apple M-series laptop (Node 20, 30 runs, 5 warmup), rendering the same 1-page invoice through every JSX-driven library.

Library mean p95 p99 Output
@imprint-pdf/react 4.3 ms 5.2 ms 5.8 ms 6.2 KB
@react-pdf/renderer 13.7 ms 15.6 ms 16.8 ms 3.3 KB
Puppeteer (warm, browser reused) 50.6 ms 51.1 ms 51.1 ms 46.9 KB
Puppeteer (cold, launch per render) 473 ms 494 ms 494 ms 46.9 KB

3.15× faster than @react-pdf/renderer on the only fair head-to-head comparison (both consume JSX, both run a layout pass). ~12× faster than warm Chromium, ~110× faster than cold Chromium - the cold number is what serverless functions actually pay, plus a 200 MB binary download.

pnpm bench                    # runs competitors + complexity + pipeline + features
pnpm bench:chromium           # opt-in, downloads Chromium on first run
pnpm bench:all                # everything, including chromium

Imperative libraries (pdfkit, pdf-lib, jsPDF) and template-based ones (pdfme) are intentionally excluded - they're a different paradigm and the comparison would flatter imprint-pdf on ergonomics while making it look slow on µs-per-glyph.

Examples

pnpm --filter @imprint-pdf/example-next-app dev

Status

1.0.0-alpha. Architecture is settled (ARCHITECTURE.md); the API is close. Pin exact versions.

Phase Versions What it means
Alpha 1.0.0-alpha.x Anything can change in any release.
Beta 1.0.0-beta.x Public API frozen modulo bugs. Soak before GA.
1.0 1.0.0 First stable release. Semver from here on.
Post-1.0 1.x.y+ Backwards-compatible features and fixes.

See STABILITY.md for the full contract.

Sponsor

imprint-pdf is Apache-2.0 and self-funded. If your company ships PDFs through it (or would like to), sponsorship unlocks the next milestones on the roadmap - including the v1.x custom PDF writer that drops the pdf-lib runtime dependency for a smaller bundle and edge-friendlier output.

GitHub Sponsors → @tamimbinhakim

License

Apache-2.0 - every package, including the compliance and print add-ons (PDF/X-4 + CMYK + ICC, PDF/UA-1 tagged PDF, PKCS#7 signing, factur-X / ZUGFeRD). No commercial seats, no time-bombed source. See LICENSE.

Contributing

pnpm install
pnpm dev         # turbo run dev across all packages
pnpm test        # vitest across the monorepo
pnpm typecheck   # composite tsc
pnpm lint        # biome check

See CONTRIBUTING.md and RELEASING.md.

© Tamim Bin Hakim and contributors.

About

Generate PDFs by writing React components with real Tailwind and typography. Runs anywhere your JavaScript runs, no headless Chromium.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages