How to create PDFs in React.JS (and have fun doing so)
I know, I know, you're thinking what does a print document format have to do with a web framework?
Well… did you know you could create PDF files as if you were developing a react web app?
This allows you to create a lot of fun stuff.
For example: if you have a blog you could offer a print version of your articles as added value. You could create a dynamically generated Resume. I created tree ownership certificates for the company I work for.
… and I also created printouts of Sudoku grids because … well, pen and paper are part of my process for solving Sudokus, I like scribbling stuff everywhere.
I'll walk you through how it works and what the pitfalls are, and by the end, you should be able to generate your own PDFs in React! Yay! Let's get started. We'll go through :
- What are the main components to get started generating your PDF?
- How we show the PDF to the end-users or allow them to download it — i.e. how do we give the user access to the PDF?
- How we layout — or at least how *I *layout — the components in the PDF
- And lastly: What are some pitfalls that need to be addressed if you want everything to go well?
Installation
Before we get started we need to install the pdf library in our react application. To do that we simply run :
yarn add @react-pdf/renderer
or, if you are using npm:
npm install @react-pdf/renderer — save
An overview of the main PDF components
There are a lot fewer tags for creating content than in HTML. There are basically 5 that we use all the time.
The first is the Document component, which would be the same as an HTML <body>
tag. This is the root node of the PDF, there is only one and it contains the whole document. It basically just says: the document starts here and ends here. And the Document component can only contain Page elements.
The Page component basically represents …a page in the document, although not quite. Most of the time it does, but if you have content that is longer than the page you can configure the component for it to wrap and so add a page break.
The Page component is where you define the page size. You do so either by specifying a page format (like 'A4') or by specifying the with and height expressed in points, the print equivalent of pixels.
Inside the page, we now have the content itself. There are three main components inside a page :
- There is a View content which is basically the equivalent of the
<div>
tag in HTML, i.e. a container that allows us to set up the layout. - The Text component is pretty much the equivalent of the
<span>
tag, and which contains text. - The Image tag is the equivalent of the
<img>
tag and contains text.
Now, let's use this to set up a React component which creates a basic "hello world" document :
const HelloWorldPDF = () => <Document>
<Page size="A4">
<View>
<Text>Hello world</Text>
</View>
</Page>
</Document>
As you can see it's just a set of React components, with the same kind of logic as we would use to create a web component. Now, let's look at how our users can view or download this document.
Setting up the entry points
In the run of the mill client-side React application, there are basically 2 ways that you can give access to the PDF document, once it has been created: by embedding the PDF via iframe, or by downloading the PDF as a file. Thankfully the pdf-react does all the heavy lifting.
The **PDFViewer **component creates an iframe showing the PDF document it wraps, like this
<PDFViewer>
<HelloWorldPDF />
</PDFViewer>
You can add width and height props to define how the iframe is displayed.
The PDFDownloadLink component enables us to allows users to download the PDF.
<PDFDownloadLink document={<HelloWorldPDF />} fileName="somename.pdf">
{
({loading}) => loading ? 'Loading' : 'Download'
}
</PDFDownloadLink>
Layout & Styling
Now the first thing to note, before we start, is that PDFs are not screens. We're so used to doing responsive design that flows, and managing mobile screens, that it's worth remembering that we know the dimensions we'll be working with. In fact, we have to specify them at the start, on the page component. You can also specify orientation and the like. But in any case you need to be aware of the size of the document you'll be working on.
For the certificate and Sudoku, my document was basically one page, without any flowing text, and so it makes sense to use absolute positioning to place the elements on the page. You can of course use the more traditional HTML flow layout if for example, you are laying out a blog page where you don't know the exact
So, how do this? Well, the react-pdf library is a wrapper over pdfkit, which supports a lot of nice features like canvas style drawing, embedding custom fonts, and of course, styling using CSS.
This means that if we want a logo in the top right we can do stuff like this :
<Image src="/src/assets/logo.png"
style={{
position:'absolute',
width: '50%',
top: 5,
right: 5
}}
/>
If we want to use styling on the text we do so using CSS styling too. However, one word of caution: if we want to use font-weight (bold, etc.) we need to make sure that we embed the corresponding fonts. We do so using the Font component, making sure we embed the font variations we want to use.
The rendering pitfall
The final point that I want to mention, and that I stumbled on quite a bit: Bear in mind that the render process for a PDF is quite a bit longer than for a webpage. A large PDF document can take several seconds to render and be several megabytes large. If we have parameters that are being injected into the component, and if these components depend on an asynchronous process, the parameters being injected might change as the render process is going on. And a number of times, the resulting PDF was completely messed up by this.
I've found it helpful to hold off on starting the render before I know I have up-to-date components. There are several ways of going about this, depending on the situation. At the very least I have a startRender state, and a useEffect hook that checks if all the parameters I'm using are correctly set. Of course, this doesn't prevent the parameters from changing once again. One way to avoid this is to use a timeout.
Key Takeways
I find React fun, and it's cool to be able to apply that React niceness to building PDF documents. We can build components, pass props, including them in different places and factoring our code, use loops
We help you better understand software development. Receive the latest blog posts, videos, insights and news from the frontlines of web development