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
8 changes: 6 additions & 2 deletions .github/workflows/react-starter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: React starter
on:
push:
branches: [main]
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', 'tools/starter_engine_fixes.py', 'tools/starter-engine-2.5.22.json', 'tools/starter-engine-2.5.24.json', '.github/workflows/react-starter.yml']
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/check-react-development.py', 'tools/prepare-react-check.mjs', 'tools/react_component_checks.py', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', 'tools/starter_engine_fixes.py', 'tools/starter-engine-2.5.22.json', 'tools/starter-engine-2.5.24.json', '.github/workflows/react-starter.yml']
pull_request:
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', 'tools/starter_engine_fixes.py', 'tools/starter-engine-2.5.22.json', 'tools/starter-engine-2.5.24.json', '.github/workflows/react-starter.yml']
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/check-react-development.py', 'tools/prepare-react-check.mjs', 'tools/react_component_checks.py', 'tools/browser-requirements.txt', 'tools/starter_standard_fonts.py', 'tools/starter_engine_fixes.py', 'tools/starter-engine-2.5.22.json', 'tools/starter-engine-2.5.24.json', '.github/workflows/react-starter.yml']
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -47,6 +47,9 @@ jobs:
- name: Check StrictMode lifecycle and downloads
if: matrix.browser == 'chrome'
run: python tools/check-react-starter.py --dist examples/react/dist-strict --out output/react-strict --browser chrome --strict
- name: Check actual TSX and stylesheet edits in the Vite development server
if: matrix.browser == 'chrome'
run: python tools/check-react-development.py --project examples/react --out output/react-development
- uses: actions/upload-artifact@v4
if: always()
with:
Expand All @@ -55,4 +58,5 @@ jobs:
output/react-baseline/
output/react-${{ matrix.browser }}/
output/react-strict/
output/react-development/
retention-days: 14
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ runtime or build step is required for ordinary JavaScript functions.

Try a complete project online: [JavaScript + Vite](https://docs.fullbleed.dev/assets/browser-starter/edit-online.html)
or [React + TypeScript](https://docs.fullbleed.dev/assets/react-starter/edit-online.html).
Edit the HTML/CSS templates, preview the result, and download a PDF. Each starter
also includes a ZIP for local development.
The React project starts with a typed component invoice and editable line items;
the JavaScript project includes HTML/CSS templates. Preview the result and
download a PDF. Each starter also includes a ZIP for local development.

Using Vue? The [Vue and TypeScript starter](examples/vue) includes editable
templates, automatic previews, downloads, and a reusable `usePdfPreview` composable.
Expand Down Expand Up @@ -125,8 +126,9 @@ data; connect your own authenticated record lookup when adapting it.

## Use it in a browser

For React, use the [React and TypeScript starter](examples/react) with automatic
previews, editable templates, cancellation, and a reusable component-lifecycle hook.
For React, use the [React and TypeScript starter](examples/react) with a
component-authored invoice, automatic previews, editable data, cancellation and
a reusable preview hook. Its dedicated print TSX files use Fullbleed's JSX runtime.

Start with the [complete browser application](examples/browser): editable HTML/CSS,
invoice and report designs, previews, cancellation, and PDF downloads.
Expand Down
6 changes: 6 additions & 0 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ For a whole document project, TypeScript options are `"jsx": "react-jsx"` and
CommonJS are supported. JSX expressions use `renderDocument` because their
TypeScript type can represent a document, element, fragment or text.

In a React/Vite application, keep print modules outside React Fast Refresh
boundaries so edits to their imported styles reach the preview. The
[React starter](../examples/react) includes the per-file pragma, Vite exclusion
and a document-aware preview hook. Its development check edits both TSX and
styles while preserving form values.

This is React-inspired synchronous composition for print output. React hooks,
events, refs, React component libraries and async components are not supported.
Fetch data before composing the document. `key` is accepted and ignored because
Expand Down
1 change: 1 addition & 0 deletions examples/react/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist/
dist-strict/
public/fullbleed/
output/
.document-check/
71 changes: 47 additions & 24 deletions examples/react/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Fullbleed React PDF starter

A React and TypeScript application with automatic PDF previews, editable HTML/CSS
templates, cancellation, and downloads. Change a form field and the actual PDF
updates after a short pause. Includes a designed invoice and three-page report;
all sample organizations, records, and amounts are fictional. MIT licensed.
A React and TypeScript application with a component-authored invoice, automatic
PDF previews, cancellation, and downloads. Change the customer, line items,
color or payment status and the actual PDF updates after a short pause. The
print template uses typed functions, props, mapped rows, conditional content and
attached styles. All sample data is fictional. MIT licensed.

## Run

Expand All @@ -21,32 +22,47 @@ Turn off **Update automatically** to render only when you choose **Generate PDF*

## Connect your data

`src/App.tsx` holds the React form, templates, and preview UI. The invoice uses
`{{customer}}` and `{{reference}}` in HTML and `{{ink}}` in CSS. The app escapes
customer text before filling the template and accepts a six-digit hex ink color.
Replace the fixed line items, totals, addresses, and dates with your data and
calculations when building a real application.

Use **Edit the HTML & CSS** to try another layout. To keep it, edit the matching
`src/invoice.html`, `src/invoice.css`, `src/report.html`, or `src/report.css` file.
Use [supported print CSS](https://docs.fullbleed.dev/css-coverage/); the engine
renders supplied static HTML/CSS rather than capturing the React page.
`src/App.tsx` holds the React form and preview UI. `src/documents/Invoice.tsx`
builds the document with Fullbleed components. Its `@jsxImportSource fullbleed/ui`
pragma selects the print JSX runtime for that file; the web UI still uses React.
Call `Invoice({ data, ink })` to compose a document and pass it to the preview
hook. Use ordinary functions for reusable print pieces. React hooks belong in
the UI, outside these synchronous document functions.

`src/documents/data.ts` defines the data, integer-cents formatting and form
validation. The demo accepts up to 20 line items and whole hours; negative rates
create credits. `src/documents/invoiceStyles.ts` holds reusable print styles,
composed with the chosen ink color. Supply your own dates and records when
building an application. Text is escaped by the component authoring layer.

Edit either print source file while Vite is running to update the PDF. The Vite
config excludes `src/documents/` from React Fast Refresh so print-component and
style changes propagate to the React form. See the React plugin's
[exclude option](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#exclude).

The design selector also includes the existing HTML/CSS invoice and three-page
report. These have an **Edit the HTML & CSS** panel and keep edits until reload.
Their source files remain `src/invoice.html`, `src/invoice.css`, `src/report.html`
and `src/report.css`. Open `?design=invoice` or `?design=report` for a direct link.
See [supported print CSS](https://docs.fullbleed.dev/css-coverage/) for layout features.

## Reuse the hook

Copy `src/usePdfPreview.ts` into your client application, install
`fullbleed@0.5.1`, and copy the runtime with
Copy `src/usePdfPreview.ts` and the `src/documents/` directory into your client application, install
`fullbleed@0.6.0`, and copy the runtime with
`npx fullbleed-browser-assets public/fullbleed`. Keep the document input stable
with `useMemo` so ordinary component updates do not schedule another render:

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

export function DocumentPreview({ html, css }: { html: string; css: string }) {
export function DocumentPreview({ 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>
<button onClick={() => { void pdf.generate(); }}>Generate PDF</button>
Expand All @@ -65,6 +81,8 @@ The hook schedules a preview after 450 ms without input changes. Use its third
argument `{ auto: false }` for on-demand generation, or `{ delayMs: 800 }` for a
longer pause. Cancel stops a pending timer or active job until the next input
change or `generate()` call. Changing preview mode also resets its 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.

It hides stale downloads immediately, aborts superseded work, ignores obsolete
results, and revokes replaced Blob URLs. Unmounting aborts the active request and
Expand Down Expand Up @@ -94,15 +112,20 @@ WebAssembly, and Web Crypto are required.

## Versions and verification

The lockfile pins Fullbleed 0.5.1 / engine 2.5.24, React 19.3.0, TypeScript 7.0.2,
The lockfile pins Fullbleed 0.6.0 / engine 2.5.24, React 19.3.0, TypeScript 7.0.2,
and Vite 8.3.2. No Fullbleed engine or package release is needed for this example.

The repository's `React starter` workflow builds the locked project and checks
real PDF downloads with an independent reader. It compares default invoice and
report PDF bytes with the installed Node package, checks mobile layout, exercises
input races, cancellation, editing, recovery, and unmount/remount, and records
real PDF downloads with an independent reader. Six component cases cover the
default invoice, paid state, edited text/color, empty items, a credit and twenty
rows across three pages. Their PDFs and every preview match the installed Node
package. The existing HTML invoice and report also keep their output checks.
The workflow checks mobile layout, input races, cancellation, editing, recovery,
and unmount/remount, and records
worker API handles and Blob URL cleanup. A separate React development build
checks the same flow with StrictMode enabled. Checks run in Chrome, Firefox and
checks the same flow with StrictMode enabled. A real Vite development-server
check edits print TSX and stylesheet files, verifies the resulting PDFs, and
checks that form values survive the updates. Checks run in Chrome, Firefox and
Playwright WebKit; WebKit testing is not branded Safari certification.

This is ordinary PDF generation and preview rendering. PDF/A, PDF/UA, PDF/X and
Expand Down
2 changes: 1 addition & 1 deletion examples/react/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Edit a React form or HTML/CSS template and generate a PDF in your browser. A complete Fullbleed React and TypeScript starter with live previews and downloads." />
<meta name="description" content="Build PDFs with typed components in React. Edit invoice data, preview and download locally, and customize the JSX document and styles in a complete TypeScript project." />
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
<title>React PDF starter | Fullbleed</title>
</head>
Expand Down
8 changes: 4 additions & 4 deletions examples/react/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions examples/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"version": "1.0.0",
"private": true,
"type": "module",
"engines": { "node": ">=22.12.0" },
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"assets": "fullbleed-browser-assets public/fullbleed",
"dev": "npm run assets && vite --host 127.0.0.1",
Expand All @@ -12,7 +14,7 @@
"preview": "vite preview --host 127.0.0.1"
},
"dependencies": {
"fullbleed": "0.5.1",
"fullbleed": "0.6.0",
"react": "19.3.0",
"react-dom": "19.3.0"
},
Expand Down
Loading
Loading