Receipt
From order to PDF
- 1
Order placed
Checkout copies the lines, prices and buyer into an order snapshot (ReceiptOrder).
- 2
Receipt page
A React page renders the snapshot as an A4 sheet: sizes in mm and pt, brand colours fixed, print CSS sets the page box.
- 3
Headless Chrome
The PDF route opens that page in Chrome (playwright-core, the installed Chrome), in print mode, and waits for fonts and images.
- 4
PDF
Chrome prints A4 with margins and a footer template for page numbers. The route returns application/pdf.
Printing from the browser (Cmd+P) uses the same page and print CSS, so it matches the PDF.
One Chrome stays running between requests; each PDF gets a fresh tab. Chrome loads the page from localhost, not through a tunnel.
Ways to make the PDF
What this feature does, against the usual alternatives.
Headless Chrome
This featurePrint the web page to PDF on the server.
+ Reuses the web components and Tailwind. What you see is what prints. Page breaks, repeated table headers and footers come from Chrome.
– Chrome must run on the server (container size, memory, cold start). The page must be reachable by Chrome, so auth needs a way in.
Browser print only
Print button, no server PDF.
+ Nothing to run on the server. Already works here (Print).
– Result depends on the customer’s browser and settings. No file to email or store.
React PDF renderer
Lay out the receipt with a PDF-specific component set.
+ No browser on the server. Precise control of the PDF.
– A second layout to build and keep in step with the web. Its own styling model, not Tailwind.
PDF service
Send HTML to a separate rendering service (self-hosted or bought).
+ Chrome-quality output without Chrome in the app.
– Another service to run or pay for. Same auth question for assets.
Questions for im-web
What needs deciding before this could move to im-web.
- Generate once when the order is paid and store the file, or render on every download?
- Where Chrome runs in im-web’s deployment, or whether a rendering service is preferred.
- How Chrome reaches the receipt page behind login: a short-lived signed link, or render from data without auth.
- Language: the receipt is English only. Norwegian and Swedish (TT) need translated labels and license texts.
- Accessibility: should the PDF be tagged for screen readers?
- Legal content: what a Norwegian receipt must contain (seller org. no., VAT per rate), checked by finance.
- Payment method and invoice details once checkout has payment (status says "Paid" today).
The code
All in src/routes/features/receipt/.
| -lib/types.ts | ReceiptOrder: the order snapshot a receipt needs |
| -lib/receipt.tsx | The receipt component, print CSS, PDF footer, seller details |
| -lib/receipt-page.tsx | Full-page view with Print and Download PDF |
| -lib/pdf.server.ts | Headless Chrome: one shared browser, A4, footer, the PDF response |
| -lib/samples.server.ts | Sample orders for the variations, with the feature’s own data |
| sample.$orderId.tsx, .pdf.ts | A sample receipt page and its PDF |