Installation
npm install json-pdf-designer
Peer deps: react and react-dom (^18.0.0 || ^19.0.0) plus
react-rnd (the canvas's drag/resize library) — all marked optional in
peerDependenciesMeta, so a backend-only install never forces them on you
(see Server-only usage). Installing <Designer>'s
side of the package means installing all three:
npm install react react-dom react-rnd
pdfjs-dist is an optional peer dependency too — needed only for the
PDF preview, which lives behind the json-pdf-designer/preview entry
point. Install it if you use the preview:
npm install pdfjs-dist
Skipping it keeps ~35MB out of your node_modules; <Designer>,
generatePdf, and downloadPdf never touch pdf.js. See
Ready-made UI components for the preview and
examples/no-preview for an app that never installs it.
Import the package's CSS once, in your app's entry point — it
styles <Designer> itself (property panel, toolbar, tabs):
import "json-pdf-designer/theme.css";
theme.css is hand-written, standalone CSS — no build step, no Tailwind
on either side, and it imports the package's reset itself, so this one
line is all of it. Skipping it is a supported mode, not a mistake:
the .jpd-* classes and data-* attributes are still in the DOM, and
you style them with whatever you use. See Theming for the
class and token contract, and json-pdf-designer/reset.css if you want
the reset without our appearance.
json-pdf-designer/style.css is gone and has no alias — the old import
fails with ERR_PACKAGE_PATH_NOT_EXPORTED. See
Migrating to 3.0.0.
Basic usage
import { useState } from "react";
import { Designer, generatePdf, downloadPdf, type Template, type Binding } from "json-pdf-designer";
import "json-pdf-designer/theme.css";
const initialTemplate: Template = {
page: { width: 210, height: 297 }, // A4 in mm
schemas: [],
};
function Report() {
const [template, setTemplate] = useState<Template>(initialTemplate);
const [bindings, setBindings] = useState<Binding[]>([]);
async function handleGenerate() {
const data = await fetchMyData(); // the real JSON that fills the fields
const pdfBytes = await generatePdf(template, data, bindings);
downloadPdf(pdfBytes, "report.pdf");
}
return (
<>
<Designer
template={template}
onChangeTemplate={setTemplate}
bindings={bindings}
onChangeBindings={setBindings}
/>
<button onClick={handleGenerate}>Generate PDF</button>
</>
);
}
onChangeTemplate/onChangeBindings accept React's setState
functional form ((prev) => next) — use the useState setter
directly, as above, so you don't lose a field if two get added in quick
succession.