Logo
Published on

Downloading React Components as PDF Files

Authors
  • Name
    Twitter

Close up of a video game controller

Users may need a way to download a specific part of the UI as a PDF file. This can include tickets, bills, or shopping lists. It’s a convenient way for them if they want to print such documents.

Recently, I had to create a button that would let our users download a list of item-specific configurations set by themselves. I was supposed to do this in the most economical way possible for performance concerns. I started researching for any React libraries to help me in this journey. I found quite a few options, yet all of them were unnecessarily vast for my use case.

TL;DR — Most probably you have encountered articles that would recommend the combination of html2canvas and jsPDF. Even though they’re very good libraries, you may not need them for such a narrow use case. In this article, I will talk about react-to-pdf (ver. 1.0.1), a concise library for downloading React components as PDF files. Please be aware that PDFs are generated from component screenshots and there is no Server-Side Rendering (SSR) supported.


Search for a Library

I started my research with some articles and as well as I asked ChatGPT for a way to download React components as PDF files. Both ways ended up that I should use html2canvas and jsPDF.

The overall logic of using these 2 libraries is converting the rendered component to an HTML canvas, embedding it into a PDF file, and downloading it.

As I mentioned earlier, I had to make an economic decision. Although jsPDF has only 8 dependencies (one is html2canvas), the unpacked size is 14.7 MB as of version 2.5.1.

Details about jsPDF (ver. 2.5.1):

Details about jsPDF from the npm page

In the end, I could get the green light only for jsPDF since html2canvas is one of the dependencies and my team thought it wouldn’t be necessary to install it additionally.

Dependencies of jsPDF (ver. 2.5.1):

Dependencies of jsPDF from the npm page

Issues along the Way

I should say that jsPDF has detailed documentation and I had no issues there. As my task was to download a specific component as a PDF file, the html method came to my rescue (or this is what I thought).

Below is an example of the usage of the html method from the documentation:

var doc = new jsPDF();  
  
doc.html(document.body, {  
   callback: function (doc) {  
     doc.save();  
   },  
   x: 10,  
   y: 10  
});

Initially, it worked well with some tweaking of scaling and positioning the component in the PDF file. However, I had an issue since our project has custom fonts, and porting them to PDFs required extra effort such.

This approach turned the rendered HTML content with its styling (except the fonts) to be used in the PDF structure. I was expecting it to create a screenshot of the component and embed it in the PDF file though.

At this point, I had to find another library for my narrow use case.


Finding react-to-pdf

After some research, I came across react-to-pdf. When I was checking some of the library details such as unpacked size and amount of dependencies, I knew that I had to give it a try.

Details about react-to-pdf (ver. 1.0.1):

Dependencies of react-to-pdf (ver. 1.0.1):

Before I jump into the implementation, I want to mention that it was surprising to see that react-to-pdf has only 2 dependencies and its unpacked size is 82.7 kB as of version 1.0.1.

After looping through some different ways to use html2canvas and jsPDF, they were right in front of me as the 2 sole dependencies.


Using react-to-pdf

You can install react-to-pdf with yarn or npm:

# Yarn  
yarn add react-to-pdf  
# NPM  
npm install --save react-to-pdf

Please be aware that PDFs are generated from component screenshots. Also, note that there is no Server-Side Rendering (SSR) supported. If you’re building in frameworks such as Next.js, you can use react-to-pdf in client components.

In terms of usage, there are 2 ways to generate PDFs: usePDF hook and default function provided from react-to-pdf.

Here is how you can use the usePDF hook:

import { usePDF } from 'react-to-pdf';  
  
const Component = () => {  
   const { toPDF, targetRef } = usePDF({filename: 'page.pdf'});  
   return (  
      <div>  
         <button onClick={() => toPDF()}>Download PDF</button>  
         <div ref={targetRef}>  
            Content to be generated to PDF  
         </div>  
      </div>  
   )  
}

Here is how you can use the default function:

import { useRef } from 'react';  
import generatePDF from 'react-to-pdf';  
  
const Component = () => {  
   const targetRef = useRef();  
   return (  
      <div>  
         <button onClick={() => generatePDF(targetRef, {filename: 'page.pdf'})}>  
            Download PDF  
         </button>  
         <div ref={targetRef}>  
            Content to be included in the PDF  
         </div>  
      </div>  
   )  
}

You can also pass some options if you want to take a step further:

import generatePDF, { Resolution, Margin } from 'react-to-pdf';  
  
const options = {  
   // default is `save`  
   method: 'open',  
   // default is Resolution.MEDIUM = 3, which should be enough, higher values  
   // increases the image quality but also the size of the PDF, so be careful  
   // using values higher than 10 when having multiple pages generated, it  
   // might cause the page to crash or hang.  
   resolution: Resolution.HIGH,  
   page: {  
      // margin is in MM, default is Margin.NONE = 0  
      margin: Margin.SMALL,  
      // default is 'A4'  
      format: 'letter',  
      // default is 'portrait'  
      orientation: 'landscape',  
   },  
   canvas: {  
      // default is 'image/jpeg' for better size performance  
      mimeType: 'image/png',  
      qualityRatio: 1  
   },  
   // Customize any value passed to the jsPDF instance and html2canvas  
   // function. You probably will not need this and things can break,   
   // so use with caution.  
   overrides: {  
      // see https://artskydj.github.io/jsPDF/docs/jsPDF.html for more options  
      pdf: {  
         compress: true  
      },  
      // see https://html2canvas.hertzen.com/configuration for more options  
      canvas: {  
         useCORS: true  
      }  
   },  
};  
  
// you can use a function to return the target element besides using React refs  
const getTargetElement = () => document.getElementById('content-id');  
  
const Component = () => {  
   return (  
      <div>  
         <button onClick={() => generatePDF(getTargetElement, options)}>  
            Generate PDF  
         </button>  
         <div id="content-id">  
            Content to be generated to PDF  
         </div>  
      </div>  
   );  
}

Here is a demo provided in the react-to-pdf documentation via Code Sandbox. I would also like to mention that the code blocks above are also from the documentation.

Conclusion

Like me, you may need to let your users download PDF files generated from components. There are several ways to do this when you check some other resources.

In this article, I focused on react-to-pdf since I liked its lightweight structure and simple usage. I hope this article helps you achieve if you have a similar task.

Resources