Embeddable report designer · React & Angular

Your users design the report. You ship the PDF.

BroadPaper drops a visual report designer into your product. You declare the data; your users lay out the document; BroadPaper handles layout, branding, pagination and a print-perfect PDF, rendered in the browser with @broadpaper/forme — no server required.

Free until you ship: everything works without a key, and PDFs carry an evaluation mark. A free 60-day key removes it while you evaluate. Open the demo or read the quick start.

pnpm add @broadpaper/react @broadpaper/core @broadpaper/blocks @broadpaper/forme

Recorded in the demo, with the waits shortened. The whole film, including rendering on your own server, is 85 seconds.

Watch the full 85-second film

§ 01What it is

A report designer that lives inside your product, not beside it.

Every serious business application ends up needing customer-facing documents: reviews, statements, factsheets, certificates, invoices. Every team ends up building a report designer for them, badly, twice. BroadPaper is the one you were going to build anyway, finished.

  1. 01

    You declare the data

    Describe your data sources with a typed schema, hand over sample data, and mount the designer in a div. About thirty lines.

  2. 02

    Your users design the document

    Drag blocks, bind fields, set conditions, pick a brand — or describe the report and let your AI model draft it. They see real data on real pages as they work, and they cannot break the grid.

  3. 03

    BroadPaper produces the file

    A saved template is JSON you keep. Render it with live data to a vector PDF, in the browser, in Node, or behind the render service.

§ 02A closer look

Blocks, not a canvas.

Page, section, row, column, block. The structure is what makes the output predictable: a heading cannot be nudged three pixels off its grid, and a table always knows which column it is in. The palette is searchable, the properties panel is declarative, and every bound field is a chip you can read. A new document starts empty, with your schemas already on the left.

The designer in design mode: block palette on the left, a portfolio-value statistic selected on the canvas, its properties on the right
Fig. 2aDesign mode. The selected statistic is bound to Portfolio › Current value; the properties panel is the block's own declared inspector.
The designer opened on a blank page, with the same schemas ready to bind
Fig. 2bAnd the empty document, which is where your own users start.
§ 03The idea underneath
§ 04Specification

Everything a commercial report designer needs. Nothing it doesn't.

Twelve things it does, and the reason each one is hard. The documentation is where the detail lives.

  • 01

    Structured document model

    Page, section, row, column, block. A tree your users cannot break, not a canvas they can.

  • 02

    Pagination we own

    Keep-together, orphans and widows, repeated table headers, running headers with page numbers.

  • 03

    What you see is what breaks

    Point preview at the engine that draws the file, with the theme's fonts embedded, and a break on screen is a break in the PDF.

  • 04

    One template, any brand

    Templates can refer to theme tokens instead of fixed colours, so a design built on tokens re-brands with one option.

  • 05

    Browserless vector PDF

    A WebAssembly engine draws selectable text and vector charts in Node or in the browser.

  • 06

    Bindings with a type system

    A hand-written expression language. No eval, checked against your schemas, with autocomplete.

  • 07

    Tables, repeaters, charts

    Sorting, limits, conditional formatting, headers that repeat. Charts interactive on screen, identical in print.

  • 08

    Custom blocks, first class

    A declarative inspector and a pure render function. Yours drag, bind, theme and print like ours.

  • 09

    Data stays yours

    Schemas in, JSON out. The SDK makes no network calls of its own — AI template design included, which uses whatever model your server calls.

  • 10

    React, Angular, .NET

    A framework-free core, thin wrappers, a render service behind HTTP and a .NET client for it, on .NET 8 and .NET 10.

  • 11

    Versioned and migratable

    Saved templates carry a schema version and migrate forward. Today's report opens next year.

  • 12

    Safe by design

    Structured rich text instead of HTML, URL validation, SSRF-guarded fetching, feature flags for tiering.

§ 05Integration

Thirty lines to a designer. One call to a PDF.

Declare a schema, hand over sample data, mount. The designer gives you back a template as JSON; the renderer turns a template and live data into a file.

  • @broadpaper/react and @broadpaper/angular, thin wrappers over one mount API.
  • @broadpaper/forme, the browserless PDF engine, in the browser or in Node.
  • @broadpaper/server, either PDF backend behind HTTP with a queue, a token and a Dockerfile.
  • BroadPaper.Client for .NET 8 and .NET 10, when the host is not JavaScript.

Quick start

report.tsxReact
import { ReportDesigner } from "@broadpaper/react";
import { renderPdfPaginated } from "@broadpaper/forme";

const client = {
  id: "client", label: "Client",
  schema: {
    fullName: { type: "string", label: "Full name" },
    value: { type: "currency", label: "Portfolio value", currency: "GBP" },
    holdings: { type: "array", label: "Holdings", itemSchema: holding }
  }
};

export function Designer() {
  return (
    <ReportDesigner
      dataSources={[client]}
      sampleData={samples}
      theme={meridian}
      themes={[meridian, hartwell]}
      onSave={(template) => api.save(template)}
    />
  );
}

// Later, anywhere: the saved template plus live data → a vector PDF.
const { pdf } = await renderPdfPaginated({ template, data, theme: meridian, registry, dataSources });
§ 06AI template design

Let your users describe the report.

Add one option and the designer gains an AI panel: a user types "a quarterly review with the holdings as a table and an allocation chart", or "make the headings navy", and gets a real template back to review — never a screenshot, never code.

  • Your model, your key, your bill. The designer calls a function you supply; your server calls Anthropic, OpenAI, Azure OpenAI or anything else. The SDK makes no network calls of its own.
  • Checked before anyone sees it. Every answer is validated against your schemas and the block catalogue — a made-up field, a prop no block reads, a change outside the selection is sent back to the model to fix, and never reaches the canvas.
  • Previewed, then one undo step. The user sees the proposal and a list of what changed, then accepts or discards it. Accepted, it goes through the same edit path as a drag and drop.
  • No data values leave. The model is sent field names, types and the design, with embedded images taken out. Not a row of your customers' data, and rendering never involves AI.

AI template design guide

Works at every licence state, in React and Angular. Examples for Anthropic, OpenAI and Azure OpenAI are in the guide.

Designer.tsx Browser
<ReportDesigner
  dataSources={[client]}
  ai={{
    // Your endpoint; the key stays on your server.
    generate: (request, signal) =>
      fetch("/api/report-ai", {
        method: "POST", body: JSON.stringify(request), signal
      }).then((r) => r.json())
  }}
/>
report-ai.ts Your server
import Anthropic from "@anthropic-ai/sdk";
import { buildTemplateAiPrompt, describeDesigner } from "@broadpaper/core/ai";
import { createRegistry } from "@broadpaper/blocks";

// Your own registry, never the one the browser describes.
const context = describeDesigner(createRegistry());

export async function POST(req: Request) {
  const request = { ...(await req.json()), context };
  const { system, messages, tool } = buildTemplateAiPrompt(request);
  const reply = await new Anthropic().messages.create({
    model: "claude-sonnet-5",
    max_tokens: 16000,
    system,
    messages,
    tools: [{
      name: tool.name,
      description: tool.description,
      input_schema: tool.inputSchema
    }],
    tool_choice: { type: "tool", name: tool.name }
  });
  // The tool's input: a template, or a refusal. BroadPaper checks it.
  const call = reply.content.find((b) => b.type === "tool_use");
  return Response.json(call?.input ?? null);
}
§ 07Client and server

Designed in a browser. Rendered wherever you like.

The designer hands you a template as JSON. That file is the whole contract: nothing in it depends on a browser, so the report a user designed on Monday is a nightly batch on Tuesday and an API endpoint on Wednesday. Same template, same paginator, same engine — the page breaks do not move.

reports.tsNode
import { renderPdfPaginated } from "@broadpaper/forme";
import { createRegistry } from "@broadpaper/blocks";

// The JSON the designer saved, straight out of your database.
const template = await db.loadTemplate(id);
const data = await db.loadClient(clientId);

const { pdf, pages } = await renderPdfPaginated({
  template, data, theme, dataSources,
  registry: createRegistry(),
  now: asAt,                  // byte-identical output
  locale: "en-GB", currency: "GBP"
});
ReviewController.cs.NET
var result = await broadpaper.RenderAsync(new RenderRequest
{
    // The same JSON, from the same table.
    Template = JsonNode.Parse(review.TemplateJson),
    Data     = RenderRequest.ToNode(review.Data),
    Now      = review.AsAt,
    Locale   = "en-GB",
    Currency = "GBP"
}, ct);

return File(result.Pdf, "application/pdf");
  • No browser on the server. The engine is Rust compiled to WebAssembly — about 7 MB, no Chromium to install, patch or keep alive. It runs in Node and in the tab.
  • .NET talks to the same engine. BroadPaper.Client targets .NET 8 and .NET 10 and calls the render service over its HTTP API — behind your own TLS — so a C# application produces these files with no JavaScript runtime and no native PDF library on the box.
  • Or no file at all. The same saved template renders as a read-only web page with charts a reader can point at — the half of the product a PDF cannot do.

Rendering on a server → The .NET client → The read-only viewer →

§ 08Examples

Three places to see it working.

The demo runs in this browser with no account. The sample application is a real host outside this repository, installing the packages the way you would. The documentation is where the detail lives. And if you are weighing it against react-pdf, pdfmake, Puppeteer or another report designer, here is how it compares — including when it is the wrong choice.

01

The interactive demo

Two starting points — a finished investment review and a blank page — three brands, a custom block, and a PDF rendered by the engine in your own tab. Nothing is uploaded anywhere.

Open the demo →

02

The sample application

One .NET back end with an Angular front end and a React front end beside it, both mounting the designer and the read-only viewer. It consumes the published packages from a real host build, which is the part a monorepo cannot prove.

codingbadger/broadpaper-sample-application →

03

The guides

Quick start, the document model, custom blocks, theming, server rendering, the render service, the .NET client and the read-only viewer. The worked examples in the quick start and the React, Angular, server and viewer guides are compiled and type-checked against the packages.

Read the guides →

§ 09By stack, and by problem

Start from where you are.

The same SDK, described for the stack you are on or the problem you arrived with. The guides are written for developers who have not heard of BroadPaper and say plainly where a simpler tool wins.

Try it. No account, no upload.

A finished report, a blank page, three brands, a custom block, and a PDF rendered in your own browser.

Not building software?

There is a hosted version of this.

BroadPaper Cloud is the same engine as a finished product: bring in a spreadsheet, pick a design, download the document. For the operations team who wants the reports rather than the report designer.

BroadPaper Cloud