Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file added docs/assets/react-starter/baseline/invoice-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/react-starter/baseline/report-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/react-starter/baseline/report-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/react-starter/baseline/report-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 57 additions & 1 deletion docs/assets/react-starter/baseline/verification.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"package": "0.5.1",
"package": "0.6.0",
"engine": "2.5.24",
"node": "v22.23.3",
"records": [
Expand All @@ -20,6 +20,62 @@
"c8fe5102e9219962ea068201be0a6730cb37de797649f4b5f44478433e79b018",
"a3e8acbc7ba4147c56d45b606d3974ec4aecbda1b5d7362451e5e8890491dc7d"
]
},
{
"design": "component-invoice",
"pages": 1,
"pdfSha256": "7ab921d0f55604f84fd519514586f8d162e2912e925ea4071a59dfbd93993f56",
"previews": [
"0fa59b3ea2938c413d8c58a02e4b08bec8349aaf93500c16b17652ba92679199"
],
"repeatedPdfPreviewsEqual": true
},
{
"design": "component-paid",
"pages": 1,
"pdfSha256": "f4efce68a8f64f3f2219b9056cf2d3d345e9622fc9b3dbc355325ddbf62f02f3",
"previews": [
"d040a9e8e435f7baa1498db9b9571508bd174b745d44eca7fc0ff5416eb46721"
],
"repeatedPdfPreviewsEqual": true
},
{
"design": "component-edited",
"pages": 1,
"pdfSha256": "709f3438a1d4b363a86bf71025a8e883670354116c0d703b46b3ff16ae668709",
"previews": [
"aba217ddb0b2b068a6c649bff1510e6627fbd82ba1febfd745a1f9b722955e52"
],
"repeatedPdfPreviewsEqual": true
},
{
"design": "component-empty",
"pages": 1,
"pdfSha256": "d4f19a2559476236fe19acf76124f28ea5faa351731c7ee5585dd59f16f07122",
"previews": [
"7d386f278ce859efde193a30ab3981ddc26e481370fb56bd49f9f703c5f21c65"
],
"repeatedPdfPreviewsEqual": true
},
{
"design": "component-credit",
"pages": 1,
"pdfSha256": "b6b5f473e297c0c5d05f5bd1711c5ce3149dfbcbdb7234d0aeb54d2372622abf",
"previews": [
"6dd0b56df6bf3bdca9c19d01fc0227dd838657c93b4c095de064cf844ce506b8"
],
"repeatedPdfPreviewsEqual": true
},
{
"design": "component-long",
"pages": 3,
"pdfSha256": "8ea549b4429c7f9b7e7b5d5b7491bbaf7b908b3cdaee63e385e2e979cdedb0d4",
"previews": [
"692d7e7ef7d2cafb225b1823fb4646e055fe6fee374f1f00c5c95e8d467ba7d4",
"61e388cdd7c7d133c647b07886bd818d91382b072323c79281c80c698983b24b",
"7648f8591967cf8be2ad40f7743cc95464606687f8bcd14ac48098d9f48560c5"
],
"repeatedPdfPreviewsEqual": true
}
]
}
Binary file modified docs/assets/react-starter/project.zip
Binary file not shown.
35 changes: 19 additions & 16 deletions docs/assets/react-starter/source.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
{
"source_repository": "https://github.com/fullbleed-engine/fullbleed-node",
"source_commit": "c35351ec5eb38e76df61c5aa3c3ce52f3855f53b",
"source_commit": "294ad01a9492d2e8c27feb4f5ed620b10ee47720",
"source_directory": "examples/react",
"package_version": "0.5.1",
"package_version": "0.6.0",
"engine_version": "2.5.24",
"react_version": "19.3.0",
"package_integrity": "sha512-NSrRDtxqMqm1/tZdEeJm1rSRSvLwL9xpX4emE0cV9wTYSIBuynoQwgV+WbC04/OXfCZpPau7vt5hS0zxYbZaMQ==",
"zip_bytes": 29619,
"zip_sha256": "26d3b55d4270b899cec4acfcc9069a53c9d7f9b400c3ff66729c1061f1e9aae8",
"package_integrity": "sha512-8tptrfaBbP4cjHohzPDPD1Lh3NuBFbRyVebn4kQhFsSX2VsMJwR6GURImfr9tbO3+iPjsZBFwZFxRYz238xc8g==",
"zip_bytes": 36270,
"zip_sha256": "a6b74368af73b2c217f9a1f2a0a6f07b0701a62185d3528f8fd6b0d9898561d1",
"files": {
"fullbleed-react-starter/.gitignore": "25d896ffa2be7ed008ad87c3956a0a88f54167d04a7cef5cf774d6989d3b5a7b",
"fullbleed-react-starter/.gitignore": "0c8c1daabb918a54d7e75061883a887e9e0039d533487324a895ebe46afe15d8",
"fullbleed-react-starter/LICENSE": "36a2115938f6e68cf0d87eccff7a6a3e57c17017e909cbd640d51aff1fa0c5a9",
"fullbleed-react-starter/README.md": "0e9684a43a829e92d62a76b8b31913dedd799ca4ec63804665268c7045a66023",
"fullbleed-react-starter/index.html": "cadf48a3476088b2efbe9012bb4b2d21fa24f1dd3e99f974f444c88fbf1ee165",
"fullbleed-react-starter/package-lock.json": "89acf51a2e9605d7d50d3a554c5b8476daa21bbe0a9e943aac1b334127737bb5",
"fullbleed-react-starter/package.json": "b2a5cfc0ce5c6bc31601708e802ac6c36c4dcd2ff762838770806d6a48206dcf",
"fullbleed-react-starter/README.md": "c4305bc2ca5def8a79e2dc786bd13d187a4307a536335a13285c38202947a633",
"fullbleed-react-starter/index.html": "cc0a93261ffb0c47e5c7aa808e03caab7c94ca2dc505d278411d96a3a703f1b3",
"fullbleed-react-starter/package-lock.json": "7a16d41978c8c5002a3b72d09c99e7a7f0d2f83c9a93cddb1ab925acd2847a73",
"fullbleed-react-starter/package.json": "d9c56c1953348c135f190e9b57cf84e54c2f960120c1c47f7959d7e1673244f1",
"fullbleed-react-starter/public/favicon.svg": "3eed6dcf1013e48bc64886fed4cf815c609ed06b404742bdacdc44c1df8d3118",
"fullbleed-react-starter/src/App.tsx": "5dbcd45d570a3e4d69f12237d252e30afe71f52cb8411c3ffe094dc72f559b9f",
"fullbleed-react-starter/src/App.tsx": "be531b4b19f86fe2b56ebb652efca23c3ce5e5c0cc656951e4e6ac13aa738d7e",
"fullbleed-react-starter/src/documents/Invoice.tsx": "df2a087615a31c95094ac8797f17e352b3bac8c0c477476989904429349cff3a",
"fullbleed-react-starter/src/documents/data.ts": "c05c98fb07deaef2140f53df4f83670f138d6cad4a453fe09f423f7adb076f70",
"fullbleed-react-starter/src/documents/invoiceStyles.ts": "1cd818aec8cf2e9def15b51ce043bfdfcfc06626fd9df63372f34f9a43160a59",
"fullbleed-react-starter/src/invoice.css": "0b310ce0f2964fd6b9b68e4afafa9307b4ff8c6b2f1fc753367dc755620ce427",
"fullbleed-react-starter/src/invoice.html": "1f065ad6694021649e61b3f666cba6dc01aacdcbd8e80e97425b383e60d7d2fe",
"fullbleed-react-starter/src/main.tsx": "f8f523ef5fc0b1e4095830466e0caa648c01f7b42036694b800c25d43d5e2c84",
"fullbleed-react-starter/src/report.css": "519591ae75c239ec602324f008dd99108ebeae0b9306df7215fa5f0442905473",
"fullbleed-react-starter/src/report.html": "46b0e975d43914c8b4492218feae09ea0ad684091a9f9ee3b18f66a87202e521",
"fullbleed-react-starter/src/style.css": "940621b782b9e39fc48f5a257c119763e11cedd158fb570a82f483ea20aa9951",
"fullbleed-react-starter/src/usePdfPreview.ts": "587e8702bd32f42abe2720f9d29dcc68e6d75aba8cb3ceacbdfcf2b583f378cc",
"fullbleed-react-starter/src/style.css": "ba8ab6a591cb4b0291b119275d9ea86da5d19fd94ed2b7279595f5498229b70f",
"fullbleed-react-starter/src/usePdfPreview.ts": "95de2c228838f2737bc5137327710a20751d5c543065f6defab44e1504ec8bdb",
"fullbleed-react-starter/tsconfig.json": "382c3c4a68901c016c3038c9653f1adaf145738c68de40a34821181ead2d7289",
"fullbleed-react-starter/vite.config.js": "4b74d0189e7f58a4c75d168367e9b8a53000555dd3de594c497f70fb5e02fff0"
"fullbleed-react-starter/vite.config.js": "2a300aafa93df7e0269d06d104960ee5ac4d64989d37d61ca482c4b88db3629c"
},
"verification_workflow": "https://github.com/fullbleed-engine/fullbleed-node/actions/workflows/react-starter.yml",
"source_verification_run": "https://github.com/fullbleed-engine/fullbleed-node/actions/runs/38757928629",
"local_ui_evidence": "https://github.com/fullbleed-engine/fullbleed-node/blob/c35351ec5eb38e76df61c5aa3c3ce52f3855f53b/verification/starters-051.json"
"source_verification_run": "https://github.com/fullbleed-engine/fullbleed-node/actions/runs/38803591833",
"local_ui_evidence": "https://github.com/fullbleed-engine/fullbleed-node/blob/294ad01a9492d2e8c27feb4f5ed620b10ee47720/verification/react-components060.json"
}
8 changes: 6 additions & 2 deletions docs/guides/javascript-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ renderer; you do not need to write HTML strings or install React.

The complete workflow uses **fullbleed 0.6.0**, wrapping engine **2.5.24**.

[Download the complete project](../assets/node-components/project.zip){ .md-button .md-button--primary }
[Try the component invoice in React](../assets/react-demo/index.html){ .md-button .md-button--primary }

[Download the complete project](../assets/node-components/project.zip){ .md-button }
[Open the component invoice](../assets/node-components/invoice.pdf){ .md-button }

[![A component-authored invoice with forest-green typography, progress bars and a total panel.](../assets/node-components/page-1.png)](../assets/node-components/invoice.pdf)
Expand Down Expand Up @@ -78,7 +80,9 @@ const result = await renderDocument(<Invoice customer="Maple & Finch" />, render

For a whole document project, TypeScript options are `"jsx": "react-jsx"` and
`"jsxImportSource": "fullbleed/ui"`. Development JSX (`react-jsxdev`) and
CommonJS are supported. JSX expressions use `renderDocument` because their
CommonJS are supported. The [React starter](react-pdf.md) connects a composed
document to a live preview and configures Vite so print TSX and style edits
refresh the PDF. JSX expressions use `renderDocument` because their
TypeScript type can represent a document, element, fragment or text.

This is React-inspired synchronous composition for print output. React hooks,
Expand Down
116 changes: 76 additions & 40 deletions docs/guides/react-pdf.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,63 @@
---
title: Generate PDFs in React with live previews
description: Build a React and TypeScript PDF editor with HTML/CSS templates, automatic previews, cancellation, and downloads. Edit the complete project online or download its source.
title: React PDF components with live previews
description: Build PDFs with typed components in React. Edit invoice data, preview and download locally, or customize the JSX document and styles in the complete online project.
---

# Generate PDFs in React
# Build PDFs with components in React

For authoring the document itself with typed function components,
[use the JavaScript components and JSX guide](javascript-components.md).
A per-file JSX pragma selects Fullbleed for a dedicated print template while
the rest of the app uses React. The editor below uses HTML/CSS templates.
Write a PDF template with typed function components and connect it to a React
form. Change line items, mark an invoice paid, or choose a color and watch the
actual PDF update. Fullbleed renders the document in a browser worker; the
starter needs no PDF server or account.

Connect a React form to a PDF preview with `fullbleed/browser`. Change a field
or an HTML/CSS template and the actual PDF updates after a short pause. The
engine renders in a browser worker; this starter needs no PDF server or account.

[Open the React demo](../assets/react-demo/index.html){ .md-button .md-button--primary }
[Open the component demo](../assets/react-demo/index.html){ .md-button .md-button--primary }
[Edit source online](../assets/react-starter/edit-online.html){ .md-button }
[Download the React project](../assets/react-starter/project.zip){ .md-button }

The project includes a designed invoice, a three-page report, a React form and
template editor, and a reusable TypeScript hook. Its sample names, records, and
amounts are fictional. The code is MIT licensed, including commercial use.
The project uses **fullbleed 0.6.0** with engine **2.5.24**. Its default invoice
uses typed props, mapped rows, conditional paid text and composed style maps.
Text is escaped by the authoring layer; amounts are calculated in integer cents.
The form accepts up to 20 rows and whole hours, including negative rates for
credits. All records and amounts are fictional. The source is MIT licensed.

## Edit the project online
## Edit the component online

Choose **Edit source online**, then **Open in StackBlitz** to load the complete
project without a local Node.js installation. Wait for **Ready** in the preview,
change the customer, and choose **Download PDF** after the preview updates.
change a row or the customer, and choose **Download PDF** after it updates.

The source editor opens `src/documents/Invoice.tsx`. Change a heading or compose
another print component there. Edit `src/documents/invoiceStyles.ts` for layout
and typography, `src/documents/data.ts` for the data model, or `src/App.tsx` for
the React form. TSX and stylesheet edits update the preview while preserving
the current form values. The launcher sends the same source files and lockfile
as the ZIP. Save or export your changes in StackBlitz to keep them.

The design selector also offers the existing **HTML/CSS invoice** and
**Community report**. Their **Edit the HTML & CSS** panel accepts templates,
with source in `src/invoice.html`, `src/invoice.css`, `src/report.html` and
`src/report.css`. Use **Reset this template** after editing one of those files
to load it into a preview that already has local edits.

## React for the app, components for the document

`src/App.tsx` uses React's JSX runtime. The print file begins with
`/** @jsxImportSource fullbleed/ui */`, selecting Fullbleed for that file.
Call `Invoice({ data })` to compose the document, then pass it to the preview
hook. The document functions are synchronous; fetch data and use React hooks
in the surrounding application.

The Vite config excludes print modules from React Fast Refresh boundaries:

```javascript
plugins: [react({ exclude: [/node_modules/, /[\\/]src[\\/]documents[\\/]/] })]
```

Open `src/App.tsx` in the **Files** panel to change the React form, or edit
`src/invoice.html` and `src/invoice.css` to change the document. After editing a
template file, expand **Edit the HTML & CSS** in the preview and choose
**Reset this template** to load it. React preserves the preview's current edits
during development updates. The online launcher uses the same source files and
lockfile as the ZIP below. It opens a temporary project in StackBlitz;
save or export your changes there if you want to keep them.
This lets edits to imported print styles propagate to the form and regenerate
the PDF. See the React plugin's [exclude option](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#exclude).
The complete project includes this configuration. The
[JavaScript component guide](javascript-components.md) explains the authoring
API, props, children and reusable styles in more detail.

<span id="run-the-project"></span>

Expand All @@ -53,26 +76,30 @@ color and wait for **Ready**. **Download PDF** saves the generated document.
Turn off **Update automatically** to generate only when you choose **Generate PDF**.
**Cancel** stops a pending preview or active render.

The lockfile pins Fullbleed npm **0.5.1** / engine **2.5.24**, React **19.3.0**,
The lockfile pins Fullbleed npm **0.6.0** / engine **2.5.24**, React **19.3.0**,
TypeScript **7.0.2**, and Vite **8.3.2**. The ZIP includes its source and lockfile;
the build downloads the published dependencies and copies their verified runtime.

## Customize the document

Open **Edit the HTML & CSS** to paste a print template. Changes stay in the tab
until reload or closing the editor. Keep a design by editing these project files:
Use **See the document component** to inspect the source while trying the form.
Keep your layout and data changes in these project files:

| File | Purpose |
| --- | --- |
| `src/App.tsx` | React fields, structured data, template selection, and preview UI |
| `src/usePdfPreview.ts` | Rendering, cancellation, stale-result protection, and URL cleanup |
| `src/invoice.html` / `src/invoice.css` | Invoice print layout |
| `src/report.html` / `src/report.css` | Three-page report print layout |
| `src/documents/Invoice.tsx` | Typed print components, rows, totals and conditional content |
| `src/documents/invoiceStyles.ts` | Attached print style maps and layout rules |
| `src/documents/data.ts` | Sample records, integer-cents formatting and form validation |
| `src/invoice.html` / `src/invoice.css` | Optional HTML/CSS invoice design |
| `src/report.html` / `src/report.css` | Optional three-page report design |
| `src/style.css` | The application interface |

The invoice fills `{{customer}}` and `{{reference}}` in HTML and `{{ink}}` in CSS.
The app escapes customer text before inserting it. Line items, amounts, dates,
and addresses are fixed samples: replace them with your data and calculations.
The component invoice accepts a typed data object and an ink color. Its rows,
totals and payment text respond to the form. Supply your own dates and records
when building an application. The optional HTML invoice fills `{{customer}}`
and `{{reference}}` in HTML and `{{ink}}` in CSS, with escaped customer text.

Use [supported Fullbleed print CSS](../css-coverage.md). The SDK consumes static
HTML/CSS rather than capturing the React page. Document scripts do not execute,
Expand All @@ -84,21 +111,24 @@ through the SDK's `fonts` and `assets` options.
Install the package and copy its runtime into your app's static directory:

```sh
npm install --save-exact fullbleed@0.5.1
npm install --save-exact fullbleed@0.6.0
npx fullbleed-browser-assets public/fullbleed
```

Copy `src/usePdfPreview.ts` from the project into your client app. Memoize its
Copy `src/usePdfPreview.ts` and the `src/documents/` directory into your client app,
or replace `Invoice` with your own document factory. Memoize the
input so unrelated component updates do not schedule a new render:

```tsx
import { useMemo } from 'react';
import { usePdfPreview } from './usePdfPreview';
import { Invoice } from './documents/Invoice';
import type { InvoiceData } from './documents/data';

export function PdfDownload({ html, css }: { html: string; css: string }) {
export function PdfDownload({ data }: { data: InvoiceData }) {
const input = useMemo(() => ({
html, css, previewDpi: 96, maxPages: 20, timeoutMs: 30_000,
}), [html, css]);
document: Invoice({ data }), previewDpi: 96, maxPages: 20, timeoutMs: 30_000,
}), [data]);
const pdf = usePdfPreview(input, '/fullbleed/');

return <section>
Expand All @@ -117,7 +147,9 @@ The default delay is 450 ms. Use the third argument `{ auto: false }` and call
`pdf.generate()` from a button for on-demand rendering. `{ delayMs: 800 }` waits
longer between edits. A new input immediately hides the old download, cancels
the preceding job, and schedules the next preview. Cancel stops work until the
next edit or explicit `generate()` call. Changing preview mode resets output.
next edit or explicit `generate()` call. Changing preview mode resets output. Pass `null` while inputs are invalid to
clear old output without starting a worker. The hook also accepts
`{ html, css, ...options }` for existing templates.

The hook releases old PDF/PNG Blob URLs and aborts active work on unmount. It
ignores obsolete results even when requests settle out of order. The starter
Expand Down Expand Up @@ -147,10 +179,14 @@ HTTPS or localhost, Web Workers, WebAssembly, and Web Crypto. The
The [source workflow](https://github.com/fullbleed-engine/fullbleed-node/actions/workflows/react-starter.yml)
checks the built project in Chrome, Firefox, and Playwright WebKit, and checks a
React development build with StrictMode enabled. It downloads real PDFs,
independently checks their text and page sizes, and compares the default invoice
and report bytes with the installed Node package. It also checks automatic and
independently checks their text and page sizes, and compares six component
cases with the installed Node package: default, paid, edited, empty, credit,
and a twenty-row invoice across three pages. Every component preview matches
the Node output too. The existing HTML invoice and report keep their byte checks. It also checks automatic and
on-demand modes, fast edits, cancellation, recovery, unmount/remount, URL cleanup,
and 1440/390/320px layouts. The docs build installs and checks the exact ZIP.
A real Vite development-server check edits TSX and stylesheet source, verifies
the resulting PDFs and preserves the React form values.
An edited-template check verifies unembedded Helvetica faces in the PDF and
visible text in its browser preview.

Expand Down
Loading
Loading