Raimonraimon@rtstic.dev

Invoice Generator

A small web app that turns a copy-pasted JSON blob, generated by pasting a prompt into ChatGPT or Claude, into a clean, downloadable PDF invoice. No signup, no backend, no stored data.

Next.jsNext.jsTypeScriptTypeScriptReact pdfReact pdf
Invoice Generator

What is Invoice Generator?

Invoice Generator is a small tool that closes the gap between "I need to send a client an invoice" and "here's a polished PDF." Copy a prewritten prompt into ChatGPT or Claude, describe the client, line items, and payment details in plain language, paste the JSON it hands back, and the app renders a full invoice, math recalculated and verified, ready to download as a real PDF.

Try it at invoice.rtstic.dev.

This is v1. A single template, built to prove the pipeline works end to end. v2 is where multiple invoice templates, a proper editor panel, and a few other things land.

The Problem

Asking an LLM directly for "an invoice PDF" is tempting, but it falls apart fast. Ask five times, get five different layouts. Worse, LLMs are not reliable arithmetic engines, so trusting a model-generated total on a real invoice is a genuinely bad idea. And in a plain chat interface, getting a clean, consistent, immediately downloadable file out the other end is a roundabout process every single time, not something that feels like a saved workflow.

The actual split that makes sense: let the LLM handle the part it is good at, turning messy natural language into structured data, and let real code handle the part it is bad at, deterministic math, consistent branding, and a reliable file output.

How It Works

JSON as the Interchange Format

JSON over TOML, mainly because LLMs are dramatically more reliable at producing syntactically valid JSON on request, it maps naturally onto nested invoice data like a line-items array, and it pairs with a straightforward runtime validator. A malformed or incomplete paste surfaces a specific, readable error instead of a silent crash or a broken render.

The Copyable Prompt

The upload flow is a small dialog with two panels: a fixed prompt matching the app's exact expected schema, with a one-click copy button, and a textarea for pasting back whatever the model returns. The prompt is explicit about returning only a clean, copy-pastable JSON snippet, no markdown fences, no commentary, since that is what models sometimes ignore if asked too casually.

Two Renderers, One Source of Data

The on-screen invoice preview is plain React and Tailwind, scaled as a whole page to fit any screen size the way a PDF viewer would, rather than letting its internal content reflow at narrow widths. The actual downloadable file is a completely separate render tree built with @react-pdf/renderer, since that library uses its own primitives and cannot reuse ordinary JSX or CSS. Both trees read from the exact same validated data object, and line-item totals are always recalculated from unit price and quantity rather than trusted from the pasted JSON, so a wrong number in the input can never silently become a wrong number on the invoice.

Design System

Built on shadcn/ui with a flat, mostly black-and-white palette, alpha-based borders instead of hardcoded grays so they composite correctly on any surface, and a base design token setup in OKLCH. The whole thing is meant to feel quiet rather than decorated.

Deployment

Fully static. No API routes, no server actions, nothing that needs a request-time server, since every piece of logic, JSON parsing, PDF generation, the logo upload, runs entirely client-side. That made a static export to Cloudflare Pages the natural fit over a full Workers/SSR setup.

Known Limitations (v1)

  • One invoice template. No layout choice yet.
  • No persistence. The pasted JSON and any uploaded logo live only in browser state for that session.
  • No pagination logic for invoices with enough line items to exceed a single page.
  • The downloadable PDF currently uses a default system font rather than the exact on-screen typeface, since embedding a custom font into the PDF renderer needs a raw font file rather than CSS.

What's Next (v2)

More templates to choose from, a proper split editor and preview view, and persistence so a draft is not lost on refresh.

Tech Stack

Next.js (App Router, static export), TypeScript, Tailwind CSS v4, shadcn/ui, @react-pdf/renderer, deployed on Cloudflare Pages.

Why I Built This

Freelance invoicing is the same five minutes of annoying repeated work every time, get the client's details right, get the math right, make it look consistent, turn it into a file someone can actually open. I wanted to see if an LLM could take the annoying unstructured part off my plate entirely while the deterministic parts stayed deterministic. This is the first working version of that idea.