A lightweight, self-hostable invoice PDF generator. No cloud, no accounts.
Try it: h1d.github.io/easypdf-lite
Existing open-source invoice generators are surprisingly heavy to self-host — 1 GB+ Docker images, dozens of environment variables, external databases. This one is a 10 MB container. One command, no config, no database.
Inspired by easy-invoice-pdf.
- Invoice form with live PDF preview and one-click download
- 10 languages, 45+ currencies, VAT/tax calculations
- Shareable invoice links (URL-encoded, compressed)
- Saved seller/buyer profiles (localStorage)
- Cyrillic/Unicode support (embedded Open Sans fonts)
- 60 e2e tests
URL API — generate invoice links programmatically
The app accepts a ?data= URL parameter containing a compressed invoice. You can generate these links from any language to create pre-filled invoices — useful for billing systems, CRMs, or automated workflows.
How it works: JSON → key compression → lz-string → URL
import LZString from "lz-string";
// Fetch the key compression map — always in sync with the app.
const KEY_MAP = await fetch("https://h1d.github.io/easypdf-lite/key-map.json")
.then((r) => r.json());
function compressKeys(obj) {
if (Array.isArray(obj)) return obj.map(compressKeys);
if (obj && typeof obj === "object") {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [KEY_MAP[k] ?? k, compressKeys(v)])
);
}
return obj;
}
const invoice = {
language: "en",
currency: "USD",
dateOfIssue: "2026-01-15",
dateOfService: "2026-01-15",
paymentDue: "2026-02-15",
seller: { name: "Acme Corp", address: "123 Main St" },
buyer: { name: "Client Inc", address: "456 Oak Ave" },
items: [
{ name: "Consulting", amount: 10, unit: "hours",
netPrice: 150, vat: 0, netAmount: 1500, vatAmount: 0, preTaxAmount: 1500 },
],
total: 1500,
};
const compressed = LZString.compressToEncodedURIComponent(
JSON.stringify(compressKeys(invoice))
);
const url = `https://h1d.github.io/easypdf-lite/?data=${compressed}`;Only include the fields you need — the app fills in defaults for everything else. The key map is served at /key-map.json and stays in sync with the source automatically. See src/types.ts for the full InvoiceData shape.
API endpoints, analytics, Sentry, newsletters, about pages, or 200 MB of node_modules.
Compared to easy-invoice-pdf
| Original | Lite | |
|---|---|---|
| Runtime | Node.js + Next.js | Bun (build) + nginx (serve) |
| UI | React 19 + shadcn/ui | Vanilla TS |
| Styling | Tailwind CSS | Plain CSS |
| @react-pdf/renderer | jsPDF (browser-side) | |
| Dependencies | 50+ | 3 runtime, 4 dev |
| Client payload | ~2 MB | ~555 KB |
| Memory (idle) | 290 MB | 4 MB |
| Docker image | 6.4 GB | 10 MB |
| node_modules | 1.3 GB | 83 MB |
docker run -d -p 3000:3000 ghcr.io/h1d/easypdf-lite:latestOpen http://localhost:3000.
bun install
bun run dev # http://localhost:3000
bun run build # bundle for production
bun run start # serve production build
# tests
bunx playwright install --with-deps chromium
bun run testMIT
