DEV Community

Abdul Ahad
Abdul Ahad

Posted on

I built a PDF invoice generator that never sends your data to a server

Most online invoice tools upload your data. Your client names, your rates, what you earn — it all sits on someone's server. For a tool whose entire job is producing one PDF, that always felt like a bad trade.

So I built PDF Bill Builder with a hard rule: nothing leaves the browser.

The stack

  • Next.js 16 (App Router) + React 19 + TypeScript
  • jsPDF + html2canvas for PDF generation — both dynamically imported, so they only load when someone actually clicks Download
  • Tailwind CSS v4 for the marketing pages; the tool itself uses inline styles (deliberate — see below)
  • Optional tier: Neon Postgres + Google OAuth + HMAC-signed tokens

Why the tool uses inline styles

The PDF is rendered by rasterising the live preview DOM with html2canvas. Any style that isn't computed at capture time silently disappears from the output. Tailwind's utility classes were fine until a CSS-layer ordering change dropped borders from the PDF but not the screen. Inline styles removed a whole class of bug where the preview and the PDF disagreed.

The A4 auto-fit problem

An invoice with 3 line items and one with 30 both need to look right on one page. The preview measures its own rendered height and applies a transform scale so content always fits A4, then html2canvas captures at the scaled size. Simple, and it beat every "paginate manually" approach I tried.

What client-side actually buys you

No server means no database of invoices, no breach surface, and no privacy policy gymnastics. It also means no server costs, which is why the free tier can be genuinely unlimited instead of a trial in disguise.

The trade-off: no cross-device sync unless you sign in. That felt like the right default for a tool people use once a month.

Code: https://github.com/Ahad-Khan2401/invoice-generator

Happy to answer questions about the jsPDF pipeline — it was the fiddliest part by far.

Top comments (0)