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
58 changes: 58 additions & 0 deletions .github/workflows/react-starter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: React starter

on:
push:
branches: [main]
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/react-starter.yml']
pull_request:
paths: ['examples/react/**', 'tools/check-react-starter.py', 'tools/prepare-react-check.mjs', 'tools/browser-requirements.txt', '.github/workflows/react-starter.yml']
workflow_dispatch:

permissions:
contents: read

jobs:
browser:
name: React starter (${{ matrix.browser }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
browser: [chrome, firefox, webkit]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: '22'
cache: npm
cache-dependency-path: examples/react/package-lock.json
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- run: npm ci --ignore-scripts --no-audit --no-fund
working-directory: examples/react
- run: npm run build
working-directory: examples/react
- run: node tools/prepare-react-check.mjs
- run: python -m pip install -r tools/browser-requirements.txt
- run: python -m playwright install --with-deps ${{ matrix.browser }}
- run: python tools/check-react-starter.py --dist examples/react/dist --out output/react-${{ matrix.browser }} --browser ${{ matrix.browser }}
- name: Build React development runtime with StrictMode
if: matrix.browser == 'chrome'
env:
NODE_ENV: development
run: npx vite build --mode strict --outDir dist-strict --sourcemap
working-directory: examples/react
- 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
- uses: actions/upload-artifact@v4
if: always()
with:
name: react-starter-${{ matrix.browser }}
path: |
output/react-baseline/
output/react-${{ matrix.browser }}/
output/react-strict/
retention-days: 14
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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.

Start with the [complete browser application](examples/browser): editable HTML/CSS,
invoice and report designs, previews, cancellation, and PDF downloads.

Expand Down
3 changes: 3 additions & 0 deletions docs/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ optional PNG previews. No PDF server or account is required.

The [runnable browser starter](../examples/browser) includes editable templates,
previews, downloads, and production build settings.
The [React and TypeScript starter](../examples/react) adds form-driven automatic
previews and a reusable hook that cancels stale work and releases output URLs
when inputs change or the component unmounts.

## Copy the runtime into your site

Expand Down
5 changes: 5 additions & 0 deletions examples/react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
dist/
dist-strict/
public/fullbleed/
output/
21 changes: 21 additions & 0 deletions examples/react/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Fullbleed Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
109 changes: 109 additions & 0 deletions examples/react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# 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.

## Run

Use Node.js 22.12 or newer for development and builds:

```sh
npm ci
npm run dev
```

Open the localhost URL printed by Vite. The browser loads the engine and fonts
from that server and renders in a Web Worker. This app does not upload document
inputs to a PDF service. Changes last until reload or closing the editor.
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.

## Reuse the hook

Copy `src/usePdfPreview.ts` into your client application, install
`fullbleed@0.3.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';

export function DocumentPreview({ html, css }: { html: string; css: string }) {
const input = useMemo(() => ({
html, css, previewDpi: 96, maxPages: 20, timeoutMs: 30_000,
}), [html, css]);
const pdf = usePdfPreview(input, '/fullbleed/');
return <section>
<button onClick={() => { void pdf.generate(); }}>Generate PDF</button>
<button onClick={pdf.cancel}>Cancel</button>
<p role="status">{pdf.error?.message ?? pdf.status}</p>
{pdf.result && <>
<a href={pdf.result.url} download="document.pdf">Download PDF</a>
{pdf.result.previews.map((url, index) =>
<img key={url} src={url} alt={`PDF page ${index + 1}`} />)}
</>}
</section>;
}
```

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.

It hides stale downloads immediately, aborts superseded work, ignores obsolete
results, and revokes replaced Blob URLs. Unmounting aborts the active request and
releases all output URLs. The example runs inside React `StrictMode`, including
its extra development setup/cleanup cycle. See React's guidance on
[Effect cleanup](https://react.dev/reference/react/useEffect#connecting-to-an-external-system).
Keep the hook in a client component; it depends on browser APIs.

Each hook owns one active worker handle. Multiple mounted components can still
render concurrently, so bound them for your target devices. Browser worker
termination does not provide a native thread-exit promise. The
[browser SDK guide](https://github.com/fullbleed-engine/fullbleed-node/blob/main/docs/browser.md)
covers limits, custom font/image bytes, hosting policy, and structured errors.

## Build and deploy

```sh
npm run build
npm run preview
```

Deploy the entire `dist/` directory to a static HTTPS host. Relative asset paths
support nested deployment URLs. The asset-copy step verifies the installed
client, worker, engine, fonts, build manifest, and notices; deploy them together.
The deployed site needs no Node server. HTTPS or localhost, Web Workers,
WebAssembly, and Web Crypto are required.

## Versions and verification

The lockfile pins Fullbleed 0.3.0 / engine 2.5.8, 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
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
Playwright WebKit; WebKit testing is not branded Safari certification.

This is ordinary PDF generation and preview rendering. PDF/A, PDF/UA, PDF/X and
VDP options are not exposed by this browser entry.
14 changes: 14 additions & 0 deletions examples/react/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<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." />
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
<title>React PDF starter | Fullbleed</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading