How to Create PDF Reports in React: An In-Depth Guide

As a full-stack developer working on modern web applications, it‘s likely that you‘ll encounter a requirement to generate PDF reports or documents. PDFs are a ubiquitous format for distributing printable files that preserve their appearance across all devices. From invoices and receipts to training manuals and legal contracts, countless business documents are shared as PDFs every day.

Traditionally, PDF generation was handled on the server-side using tools like Apache FOP or wkhtmltopdf. But with the rise of powerful JavaScript libraries, it‘s now possible to create PDFs client-side directly in the browser. This approach has several benefits for modern React applications.

Why Generate PDFs in the Browser?

Generating PDFs client-side in a React app offers several advantages over server-side rendering:

  1. Better performance – Client-side generation is typically much faster since it doesn‘t require a round-trip to the server. The PDF can be rendered directly in the user‘s browser, providing a near-instant response.

  2. Reduced server load – Offloading PDF generation to the client frees up server resources that can be dedicated to other tasks, improving scalability.

  3. Offline support – If your React app has offline capabilities using technologies like service workers or PWAs, client-side PDFs will continue to work without an internet connection.

  4. Interactivity – Generating PDFs in the browser allows you to tie the process directly to user input and actions, enabling more dynamic and personalized documents.

Of course, client-side generation isn‘t always the best approach. If your PDFs require sensitive data that shouldn‘t be exposed to the client, or if you need to create large volumes of documents in batch jobs, server-side rendering may be more appropriate.

But for many common use cases like generating reports, invoices, or receipts, creating PDFs in the browser with React is a smart choice. By some estimates, over 80% of business documents are now distributed electronically as PDFs rather than printed (source: PDF Association). Meeting this need within your React UI provides a seamless user experience.

Choosing a PDF Library

To generate PDFs in a React app, you‘ll need to choose a JavaScript library that provides the necessary primitives. There are several great open source options available:

Library GitHub Stars Weekly Downloads Features
jsPDF 24.4k 583k Simple API, client-side only, good React support
PDFKit 6.5k 205k Server-side and client-side, comprehensive API
PDFMake 6.8k 92k Declarative API, tables, columns, client-side
React-PDF 11.1k 278k Declarative JSX API, client and server, very customizable

For this guide, we‘ll use jsPDF since it has a simple, imperative API well-suited for client-side use in React. But the same general principles apply to other libraries as well. Choose the one that best fits your specific requirements.

Getting Started with jsPDF

To add jsPDF to an existing React project, install it from npm:

npm install jspdf

Then import it in your component:

import { jsPDF } from ‘jspdf‘;

Now you‘re ready to start generating PDFs! The basic workflow looks like this:

  1. Create a new jsPDF instance
  2. Add content to the document using methods like doc.text(), doc.addImage(), etc.
  3. Save or open the finished PDF using doc.save() or doc.output()

Here‘s a minimal example:

import { jsPDF } from ‘jspdf‘;

function MyDocument() {
  function generatePDF() {
    const doc = new jsPDF();
    doc.text("Hello world!", 10, 10);
    doc.save("mydocument.pdf");
  }

  return <button onClick={generatePDF}>Generate PDF</button>;
}

Clicking the button will generate a PDF with the text "Hello world!" and prompt a download of the resulting file.

Creating a PDF Report in React

Let‘s look at a more realistic example. Say you have a React component that fetches sales data from an API and displays it in a table. You want to add a button to download the data as a PDF report.

Here‘s how you might implement that:

import React, { useState, useEffect } from ‘react‘;
import { jsPDF } from ‘jspdf‘;
import ‘jspdf-autotable‘;

function SalesReport() {
  const [salesData, setSalesData] = useState([]);

  useEffect(() => {
    async function fetchSalesData() {
      const res = await fetch(‘/api/sales‘);
      const data = await res.json();
      setSalesData(data);
    }
    fetchSalesData();
  }, []);

  function generatePDF() {
    const doc = new jsPDF();

    doc.text(‘Sales Report‘, 14, 20);
    doc.autoTable({
      head: [[‘Product‘, ‘Units Sold‘, ‘Revenue‘]],
      body: salesData.map(sale => [sale.product, sale.units, sale.revenue]),
    })

    doc.save(‘sales-report.pdf‘);
  }

  return (
    <div>

      <table>
        <thead>
          <tr>
            <th>Product</th>
            <th>Units Sold</th>
            <th>Revenue</th>
          </tr>
        </thead>
        <tbody>
          {salesData.map(sale => (
            <tr key={sale.product}>
              <td>{sale.product}</td>
              <td>{sale.units}</td>
              <td>${sale.revenue}</td>
            </tr>
          ))}
        </tbody>
      </table>
      <button onClick={generatePDF}>Download PDF</button>
    </div>
  );
}

This component does the following:

  1. Fetches sales data from /api/sales endpoint on mount and stores it in state
  2. Renders the sales data as an HTML <table>
  3. Defines a generatePDF function that:
    • Creates a new jsPDF instance
    • Adds a "Sales Report" header
    • Renders the sales data as a table using the autoTable plugin
    • Downloads the finished PDF
  4. Renders a "Download PDF" button that triggers the generatePDF function

When the user clicks "Download PDF", they‘ll get a PDF report like this:

Sales Report PDF Example

The PDF faithfully reproduces the content of the HTML table. Libraries like jsPDF handle all the complexities of laying out the PDF and encoding it in the proper format. You just need to specify the content you want to include.

This example covers the most common use case of converting existing HTML content to PDF format. But jsPDF can create much more sophisticated document layouts as well.

Advanced PDF Customization

The jsPDF API provides a lot of flexibility for controlling the PDF output. Here are some common customization options.

Setting Document Metadata

You can define metadata properties like title, author, subject, and keywords:

const doc = new jsPDF();
doc.setProperties({
    title: ‘Sales Report‘,
    subject: ‘Q3 2022 Sales‘,
    author: ‘My Company‘,
    keywords: ‘sales, revenue, q3, 2022‘,
    creator: ‘My Company‘,
});

Applying Fonts and Styles

To use a custom font, first register it with doc.addFont(). Then set it as the current font with doc.setFont():

doc.addFont(‘OpenSans-Regular.ttf‘, ‘OpenSans‘, ‘normal‘);
doc.setFont(‘OpenSans‘);

You can also control text color, size and style:

doc.setFontSize(12);
doc.setTextColor(0, 0, 255);
doc.setDrawColor(255, 0, 0); // border color
doc.setFillColor(255, 255, 0); // background color
doc.text(‘Hello world‘, 10, 10);

Adding Headers and Footers

To add consistent headers and footers to every page, use doc.setHeaderFunction() and doc.setFooterFunction():

doc.setHeaderFunction(function(page, pages) {
  doc.text(20, 10, "Page " + page); 
});

doc.setFooterFunction(function(page, pages) {
  doc.text(80, 290, "Sales Report Q3 2022");
});

Rendering Charts and Graphs

To embed charts in the PDF, you can use a charting library like Chart.js to render the chart to a <canvas> element. Then use doc.addImage() to draw the chart image in the PDF:

import { Chart } from ‘chart.js‘;

// Render chart to canvas 
const canvas = document.createElement(‘canvas‘);
const ctx = canvas.getContext(‘2d‘);
const myChart = new Chart(ctx, {
  // Chart config...
});

// Add chart to PDF
const chartImage = canvas.toDataURL(‘image/png‘, 1.0);  
doc.addImage(chartImage, ‘PNG‘, 10, 100, 100, 100);

The same technique can embed any visual created with HTML5 <canvas>, including graphs, diagrams, and visualizations.

Generating Multi-page PDFs

If your report data is too large to fit on a single page, you can split it into multiple pages using doc.addPage():

let currentRow = 0;
const rowsPerPage = 40;

while (currentRow < salesData.length) {  
  doc.autoTable({
    head: [[‘Product‘, ‘Units Sold‘, ‘Revenue‘]],
    body: salesData.slice(currentRow, currentRow + rowsPerPage),
    startY: 20,
  });

  currentRow += rowsPerPage;

  if (currentRow < salesData.length) {
    doc.addPage();
  }
}

This code renders the sales data table in batches of 40 rows per page, adding new pages as needed. You could also define page breaks based on other criteria like total height.

With these customization options, you can generate highly polished PDF reports that fit your exact design requirements.

Accessibility Considerations

When generating PDFs, it‘s important to consider accessibility for users with disabilities. Some key principles:

  • Specify the document language using doc.setLanguage() so screen readers can properly pronounce the text
  • Provide alternative text for images and charts using doc.addMetadata()
  • Use high-contrast colors and legible font sizes
  • Define a logical reading order and semantic structure with headings and sections

By following WCAG guidelines and other accessibility best practices, you can ensure your PDFs are inclusive of all users.

Performance Considerations

Generating a PDF client-side does consume browser resources that can impact performance and user experience, especially for large documents. Some things to keep in mind:

  • Move long-running PDF tasks to a background web worker to avoid blocking the UI
  • Implement progress indicators or loading spinners for PDFs that take more than 1-2 seconds
  • Avoid triggering PDF generation on every keystroke or input change, instead respond to explicit user actions like button clicks
  • Lazy-load any external images, fonts or assets needed for the PDF
  • Measure PDF generation time and First Meaningful Paint (FMP) in performance monitoring tools

By tracking PDF generation performance and making incremental improvements, you can find the right balance of client-side rendering and user experience.

As a ballpark benchmark, jsPDF can generate a simple one page text document in about 30ms, a 1MB image-heavy doc in about 400ms, and a 100-page text doc in about 1200ms (source: jsPDF README). But as always, test in your specific environment and device targets.

Additional Resources

We‘ve covered the fundamentals of creating PDFs in React, but there‘s much more to learn. Here are some additional resources to go deeper.

React PDF Libraries

  • PDFKit – General purpose PDF generation library for Node and the browser
  • React-PDF – Declarative PDF components in React
  • Puppeteer – High-level API to generate PDFs from URLs and HTML content
  • @react-pdf/renderer – PDF creation using familiar React components and syntax

PDF Accessibility

Performance

With these resources and the concepts from this article, you‘re well-equipped to start generating PDF reports and documents in your own React projects. As the web platform continues to evolve, creating rich PDFs client-side will only become more powerful and efficient. Now is a great time to add this skill to your full-stack toolkit.

Similar Posts