PDF invoices with JSX
JSX isn't tied to HTML. Compose a PDF from components, just like a page, and let a tiny writer turn it into bytes. Edit the invoice below and watch the PDF update.
- Components & async
thiscontext- ~250-line PDF writer
- No embedded fonts
How it works
- JSX builds a tree
jsx-async-runtime turns every element into a plain object like
{ tag, props }, which doesn't have to become HTML. - renderPdf() walks it
It calls components (also async ones, with
thisas context) and collects drawing primitives like<Text>and<Rect>. - pdf.js writes the bytes
Numbered objects, operators like
BT /F1 10 Tf … Tj ETand a table of byte offsets, returned by the route as aResponse.
InvoicePdf.jsxJSXfunction AmountDue({ gross, due }) {
return (
<View x={270}>
<Rect width={229} height={96} fill="#6d28d9" radius={10} />
<View x={18} y={24}>
<Text size={7.5} bold color="#ddd6fe">
AMOUNT DUE
</Text>
<Text y={32} size={24} bold color="#ffffff">
{euro.format(gross)}
</Text>
<Text y={54} size={9} color="#ddd6fe">
Please pay by {formatDate(due)}
</Text>
</View>
</View>
);
}<Document title>- The root element of the PDF.
<Page>- Starts a new A4 page.
<View x y>- Moves its children, so components draw relative to their own origin.
<Text x y size bold color align>- One line of Helvetica text.
<Rect … radius>- A box, filled or stroked, with optional rounded corners.
<Circle cx cy r>- A circle, drawn with four Bézier curves.
<Line x1 y1 x2 y2>- A straight line.