react-pdf vs an embedded report designer
Both produce a PDF from a React application, and comparing them feature by feature misses the point. They answer different questions, and the question they answer is about your organisation rather than your stack.
Two shapes, not two libraries
Almost every comparison of these two starts with a feature table, and a feature table is the wrong instrument. Strip both back and the difference is one sentence:
With react-pdf, the layout lives in your repository and changes at the speed of your release process. With a report designer, the layout lives in your database and changes at the speed of a person dragging something.
Everything else follows from that. Testing, review, who can make a change, what a customer can be promised, what happens when the fifth tenant wants a different arrangement — all of it is downstream of where the layout is stored.
Which means the question is not “which is better”. It is “does anybody outside engineering need to change these documents”. If the answer is genuinely no, react-pdf is smaller, free, and the better engineering decision, and you can stop reading here with our blessing.
What react-pdf actually is
A React renderer that targets PDF instead of the DOM. You compose Document, Page,
View, Text, Image and Link; it lays them out with its own
flexbox implementation and draws the result. It does not render HTML or CSS — the styles are a React Native-ish
subset, not a browser — which is the single most common surprise for people arriving from print stylesheets.
import { Document, Page, View, Text, StyleSheet } from "@react-pdf/renderer";
const styles = StyleSheet.create({
page: { padding: 40, fontSize: 11 },
row: { flexDirection: "row", borderBottom: "1 solid #ddd", paddingVertical: 4 },
cell: { flex: 1 }
});
export const Statement = ({ client, lines }: Props) => (
<Document>
<Page size="A4" style={styles.page}>
<Text>Statement for {client.name}</Text>
{lines.map((line) => (
<View key={line.id} style={styles.row}>
<Text style={styles.cell}>{line.description}</Text>
<Text style={styles.cell}>{line.amount}</Text>
</View>
))}
</Page>
</Document>
); What you get, and it is a lot:
- A real layout engine, so text wraps and boxes grow. No cursor, no manual page-break arithmetic.
- Automatic page wrapping, with page-break control on individual elements.
- Font embedding, browser and Node rendering, and an MIT licence.
- Components. Your document is composable, reviewable and typed, which is a genuinely nice way to work.
What it is not:
- Not HTML. Anything you know about CSS print layout transfers partially at best, and third-party chart libraries that emit SVG or DOM do not drop in.
- Not a table library. There is no table primitive; tables are composed from
Views, and a header that repeats after a page break is code you write. - Not an editor, and not trying to be. That is a legitimate scope decision, not a gap.
What an embedded report designer actually is
The inversion. Instead of writing the document, you write down what could be in one — a typed schema of fields — and hand a visual editor to whoever should be arranging them. What comes back is a serialisable document model, not code.
// The BroadPaper equivalent is not a component. It is a declaration of
// what exists, and then a component that lets somebody else arrange it.
import type { DataSource } from "@broadpaper/core";
export const dataSources: DataSource[] = [
{
id: "client",
label: "Client",
schema: { name: { type: "string", label: "Client name" } }
},
{
id: "statement",
label: "Statement",
schema: {
lines: {
type: "array",
label: "Lines",
itemSchema: {
type: "object",
fields: {
description: { type: "string", label: "Description" },
amount: { type: "currency", label: "Amount", currency: "GBP" }
}
}
}
}
}
];
// …and then <ReportDesigner dataSources={dataSources} onSave={save} />.
// Nothing above says where anything goes on the page. That is the point. Notice what is absent from that file. There is no page, no ordering, no styling and no mention of where anything goes. That is the entire contract: you say what exists, somebody else says where it goes, and the SDK is responsible for everything in between — measurement, page breaks, repeated table headers, branding, fonts and the finished file.
The output of the editor is JSON, which is the property that makes the rest tractable. A layout you can store is a layout you can version, diff, copy between tenants, validate on the way in and roll back. A layout that is code is none of those things without a deployment.
The cost of the seam
Teams almost always underestimate the middle ground — “we'll just make our react-pdf components configurable”. It is worth pricing properly, because it is the option that looks cheapest and is not.
Making a code-defined document user-editable means building, in order:
- A serialisable format. Some JSON your components can be driven by. This is a document model, and designing one is a week you will spend twice.
- An interpreter. The thing that turns that JSON back into components, including conditionals and lists.
- An editor. Drag, drop, select, undo, redo, keyboard, a properties panel, and a preview that agrees with the output.
- A binding layer. Some way for a non-developer to say “this text is the client's name”, with a picker and some type checking, without letting them write JavaScript.
- Validation. Because now users can save documents that cannot render, and you have to catch that before a customer does.
- Versioning. Because saved layouts outlive the schema they were written against.
That list is a product. It is the product BroadPaper is. Building it is entirely reasonable if reports are your differentiator — and a poor use of a quarter if they are not.
There is a fourth option people forget: keep the documents in code and get faster at changing them. A tight deployment pipeline and a component library for documents genuinely solves this for a lot of teams, and it is the right answer when the changes are frequent but always come through you.
How to decide
| If this is true | Then |
|---|---|
| Engineering owns every document and always will | react-pdf. Free, small, composable, and you skip a dependency. |
| Layouts change, but only through your team | react-pdf, plus effort spent on making releases cheap. |
| Customers ask for layout changes, and you keep saying yes | A designer. You are already doing the work; this is about who does it. |
| Every tenant needs its own branding | Either — but check how much of your react-pdf code is already a theme system in disguise. |
| Documents are mostly long tables | A designer, or expect to write repeated headers and break rules yourself. |
| Reports are a headline feature you sell on | A designer, or build one deliberately and resource it like a product. |
| You need the screen and the file to break in the same places | A designer with its own paginator — otherwise the preview is an approximation. |
Facts about react-pdf here come from react-pdf.org and its repository, checked on the date at the top of this page. If something has changed, tell us at hello@broadpaper.com and we will correct it.
Using both
This is not a migration story and there is no converter — a react-pdf document is code and a BroadPaper report is data, so nothing translates between them. The teams who end up with both do it deliberately:
- In code: documents whose layout is prescribed by somebody else. A statutory notice, a regulator's form, an insurance schedule with a legally required order. You want those in version control where a reviewer can see them change.
- In the designer: everything customer-facing and negotiable. Client reports, statements, proposals, packs, certificates — the documents where “can we move this” is a reasonable thing for a customer to ask.
If you want the two compared row by row rather than argued about, here is the table. If you are specifically trying to work out the architecture for user-designed reports in a React app, that has its own guide.
Questions
- What is the difference between react-pdf and a report designer?
- react-pdf is a renderer: you write the document as React components and it produces a PDF. A report designer is an editor: your users lay the document out visually and it is stored as data. The difference is not features, it is who is allowed to change a layout and how long that takes.
- Is react-pdf good enough for production reports?
- Yes, for documents your team owns. It has its own flexbox layout, automatic page wrapping, page-break control on elements, font embedding and browser or Node rendering, and it is MIT-licensed. The things it deliberately does not do are render HTML and CSS, and provide an editor.
- Can I let users edit react-pdf templates?
- Only by building the editor yourself. Because the document is JSX, user-editable layouts mean inventing a serialisable format, writing a visual editor that produces it, and writing an interpreter that turns it back into components — at which point you have built a report designer, and you own its bugs and its pagination.
- Can react-pdf and BroadPaper coexist?
- Yes, and it is the common outcome. Documents whose layout is prescribed — a regulatory form, a statutory notice — are better in code where a reviewer sees them change. The customer-facing documents beside them are the ones worth handing over.
- Which one handles tables better?
- react-pdf has no table component; tables are composed from Views, and repeating a header across a page break is something you implement. BroadPaper has a table block with repeated headers, totals, column widths and long tables measured in batches so ten thousand rows still break where they should. If your documents are mostly tables, that gap is the whole decision.
Try the other shape before you commit to one.
The designer runs in your own browser with no account, and the quick start is about thirty lines to the same thing inside a Vite React app.