How do I get started with react-pdf?
Updated
Install @react-pdf/renderer with npm, then build a document out of React components and render it to a file, a stream, or a blob — the same document works in Node or in the browser. That is the whole workflow react-pdf is built around: you describe the PDF with the layout primitives you already use in React, and the renderer produces the actual PDF.
This guide covers the install, a minimal working document, the three render targets, and the points where people usually get stuck.
What react-pdf actually is
react-pdf is a React renderer for creating PDF files on the browser and server. It is not a PDF viewer and not a wrapper around an existing PDF — you author the document in JSX and it renders out as a PDF.
Two things make it feel familiar:
- Layout you already know. Yoga brings flexbox to the page, so rows, columns, wrapping, and absolute positioning behave the way they do in React Native-style layout.
- Beyond text. Custom fonts, images, SVG, form fields, hyphenation, and page-break control are all supported.
Install
npm install @react-pdf/renderer
That single package is the entry point. The official docs and the Playground are the two places to look next — the Playground lets you edit a document and see the output without setting up a project.
Write your first document
A react-pdf document is a tree of components. The core ones you need for a first pass:
| Component | Role |
|---|---|
Document |
Root of the PDF |
Page |
A single page; holds size and style |
View |
A layout container (the flexbox box) |
Text |
Any text content |
A minimal document looks like this:
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: { padding: 30 },
row: { flexDirection: 'row' },
label: { width: 100 },
});
const Invoice = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.row}>
<Text style={styles.label}>Invoice</Text>
<Text>#001</Text>
</View>
</Page>
</Document>
);
Two things to notice:
- Styles are not CSS.
StyleSheet.createtakes a subset of CSS expressed as objects. Flexbox properties work; many web-only properties do not. - Text must live inside
Text. You cannot drop a raw string into aViewand expect it to render.
Render it
The same document renders to a file, a stream, or a blob — in Node or straight in the client. Which one you pick depends on where the PDF is going.
On the server (Node)
Use renderToFile when you want a file on disk, or renderToStream when you want to pipe the PDF into an HTTP response:
import { renderToFile } from '@react-pdf/renderer';
import Invoice from './Invoice';
await renderToFile(<Invoice />, './invoice.pdf');
Expected result: invoice.pdf appears at the given path. For an API endpoint, renderToStream lets you send the bytes directly to the client without writing to disk first.
In the browser
Use PDFDownloadLink for a download button, or usePDF when you want the blob and control the timing yourself:
import { PDFDownloadLink } from '@react-pdf/renderer';
import Invoice from './Invoice';
<PDFDownloadLink document={<Invoice />} fileName="invoice.pdf">
{({ loading }) => (loading ? 'Preparing…' : 'Download invoice')}
</PDFDownloadLink>
Expected result: clicking the link downloads invoice.pdf. The loading flag matters — rendering is asynchronous, so the link is not ready on first paint.
Verify it worked
- Server: check the file exists and opens in a PDF reader. If you used a stream, confirm the response has the right content type and the browser renders it inline rather than downloading garbage.
- Browser: click the link and open the downloaded file. If the button never leaves the loading state, the document threw during render — check the console.
Common sticking points
- Assuming web CSS carries over. Flexbox works; a lot of everything else does not. Build layout with
Viewand flex properties rather than reaching for margins and floats. - Putting text outside
Text. Raw strings in aViewwill not render. - Forgetting the render is async. In the browser, handle the
loadingstate fromPDFDownloadLinkorusePDF; on the server, await the render call. - Fonts. Custom fonts are supported, but you have to register them before they appear — a missing font shows up as fallback text, not an error.
- Page breaks. Long content does not always break where you want. Page-break control is available, so use it rather than fighting the default flow.
Where to go next
Start in the Playground to iterate on layout without a build step, then move the document into your app. The docs cover the component set, styling rules, and the rendering APIs in more depth than any single example can.