pdf libraries

pdf libraries

Javascript

Javascript

How to Generate PDF Reports from HTML with React-PDF

Marcelo Abreu, founder of pdforge

Marcelo | Founder

Marcelo | Founder

Nov 14, 2024

Nov 14, 2024

Overview of React-PDF: A Powerful PDF Library for React

Generating PDF reports from HTML content is a ubiquitous need in modern SaaS applications, especially for delivering invoices, receipts, and detailed analytics to users. React-PDF is a robust PDF library for React that empowers developers to create intricate PDF documents using familiar React components. Built on top of PDFKit, React-PDF leverages this solid foundation to provide a declarative and intuitive API for PDF generation.

They have a well structured documentation that you can check out here.

Comparing React-PDF with Other PDF Libraries

When selecting a PDF generation tool, it’s essential to understand how React-PDF stacks up against other libraries. PDF tools generally fall into two categories: canvas-like libraries and browser-oriented libraries.

Trend of download for javascript canva-like pdf libraries

Canvas-Like PDF Libraries:

  • pdf-lib: A lightweight library for creating and modifying PDFs in JavaScript without external dependencies.

  • PDFKit: A feature-rich PDF generation library for Node.js, offering low-level control over PDF creation.

  • pdfmake: Allows creation of PDFs using a declarative syntax, suitable for both Node.js and browser environments.

  • jsPDF: A client-side library for generating PDFs directly in web browsers.

Trend of download for javascript browser-oriented pdf libraries

Browser-Oriented Libraries:

  • Puppeteer: A Node.js library providing a high-level API to control Chrome or Chromium over the DevTools Protocol.

  • Playwright: Similar to Puppeteer but supports multiple browsers, including Chromium, Firefox, and WebKit.

React-PDF distinguishes itself by combining the declarative nature of React with the power of PDFKit. This synergy allows for seamless integration within React applications, making the process of converting HTML to PDF more intuitive and maintainable.

html to pdf guide using react-pdf
html to pdf guide using react-pdf

Setting Up Your Project with React-PDF and Node.js

To get started with React-PDF, ensure that Node.js and npm are installed on your machine. Begin by creating a new React application:

npx create-react-app pdf-report-generator
cd

Install React-PDF and its necessary dependencies:

npm

Project Folder Structure

Organizing your project enhances scalability and maintainability. A recommended folder structure might look like this:



  • components/: Contains all React components, including your PDF templates.

  • data/: Holds data files or mocks that feed into your components.

  • App.js: The main application file where components are assembled.

Building the HTML Template and Styling for PDF

Let’s create a complete invoice template as an example. In src/components/Invoice.js, build the PDF document using React-PDF components:

// src/components/Invoice.js
import React from 'react';
import {
  Document,
  Page,
  Text,
  View,
  StyleSheet,
  Font,
} from '@react-pdf/renderer';

// Register a custom font
Font.register({
  family: 'Open Sans',
  src: 'https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0b.woff2',
});

const styles = StyleSheet.create({
  page: {
    padding: 40,
    fontFamily: 'Open Sans',
    fontSize: 12,
    lineHeight: 1.6,
    color: '#333',
  },
  header: {
    marginBottom: 20,
  },
  invoiceTitle: {
    fontSize: 24,
    marginBottom: 20,
  },
  section: {
    marginBottom: 10,
  },
  table: {
    display: 'table',
    width: 'auto',
    marginTop: 20,
  },
  tableRow: {
    flexDirection: 'row',
  },
  tableColHeader: {
    width: '25%',
    borderStyle: 'solid',
    borderWidth: 1,
    backgroundColor: '#f0f0f0',
    padding: 5,
  },
  tableCol: {
    width: '25%',
    borderStyle: 'solid',
    borderWidth: 1,
    padding: 5,
  },
  tableCellHeader: {
    fontSize: 12,
    fontWeight: 'bold',
  },
  tableCell: {
    fontSize: 10,
  },
});

const Invoice = ({ data }) => (
  <Document>
    <Page style={styles.page}>
      <View style={styles.header}>
        <Text style={styles.invoiceTitle}>Invoice</Text>
      </View>
      <View style={styles.section}>
        <Text>Date: {data.date}</Text>
        <Text>Invoice #: {data.invoiceNumber}</Text>
        <Text>Customer: {data.customerName}</Text>
      </View>
      <View style={styles.table}>
        <View style={styles.tableRow}>
          <View style={styles.tableColHeader}>
            <Text style={styles.tableCellHeader}>Item</Text>
          </View>
          <View style={styles.tableColHeader}>
            <Text style={styles.tableCellHeader}>Quantity</Text>
          </View>
          <View style={styles.tableColHeader}>
            <Text style={styles.tableCellHeader}>Price</Text>
          </View>
          <View style={styles.tableColHeader}>
            <Text style={styles.tableCellHeader}>Total</Text>
          </View>
        </View>
        {data.items.map((item, index) => (
          <View style={styles.tableRow} key={index}>
            <View style={styles.tableCol}>
              <Text style={styles.tableCell}>{item.description}</Text>
            </View>
            <View style={styles.tableCol}>
              <Text style={styles.tableCell}>{item.quantity}</Text>
            </View>
            <View style={styles.tableCol}>
              <Text style={styles.tableCell}>${item.price.toFixed(2)}</Text>
            </View>
            <View style={styles.tableCol}>
              <Text style={styles.tableCell}>
                ${(item.quantity * item.price).toFixed(2)}
              </Text>
            </View>
          </View>
        ))}
        <View style={styles.tableRow}>
          <View style={[styles.tableCol, { borderTopWidth: 2 }]}>
            <Text style={styles.tableCell}>Total</Text>
          </View>
          <View style={[styles.tableCol, { borderTopWidth: 2 }]}></View>
          <View style={[styles.tableCol, { borderTopWidth: 2 }]}></View>
          <View style={[styles.tableCol, { borderTopWidth: 2 }]}>
            <Text style={styles.tableCell}>
              ${data.items.reduce((sum, item) => sum + item.quantity * item.price, 0).toFixed(2)}
            </Text>
          </View>
        </View>
      </View>
    </Page>
  </Document>
);
export default Invoice;

This template demonstrates how to create a dynamic HTML structure for PDF content using React components. Styling is managed through StyleSheet, ensuring consistency across the document. Responsive layouts are handled using Flexbox properties, and images and fonts are optimized by registering only necessary assets.

Generating and Exporting PDFs with React-PDF

In src/App.js, render and export the PDF document:

// src/App.js
import React from 'react';
import { PDFDownloadLink } from '@react-pdf/renderer';
import Invoice from './components/Invoice';
import invoiceData from './data/invoiceData';

function App() {
  return (
    <div>
      <h1>Invoice Generator</h1>
      <PDFDownloadLink
        document={<Invoice data={invoiceData} />}
        fileName="invoice.pdf"
      >
        {({ loading }) =>
          loading ? 'Generating PDF...' : 'Download Invoice'
        }
      </PDFDownloadLink>
    </div>
  );
}

export default App;

The PDFDownloadLink component provides a seamless way to export PDFs on the frontend, enhancing the user experience.

To incorporate custom data from JavaScript, update src/data/invoiceData.js:

// src/data/invoiceData.js
const invoiceData = {
  date: '2024-11-14',
  invoiceNumber: 'INV-001',
  customerName: 'Jane Smith',
  items: [
    { description: 'Service A', quantity: 3, price: 39.99 },
    { description: 'Service B', quantity: 2, price: 59.99 },
  ],
};

export default invoiceData;

Exporting PDFs on the Backend with Node.js

For server-side PDF generation, which is crucial for automating PDF creation at scale, set up a Node.js backend using ESM modules. Install the necessary package:

npm

Configure your server in server.js:

// server.js
import express from 'express';
import React from 'react';
import { renderToStream } from '@react-pdf/node';
import Invoice from './src/components/Invoice.js';
import invoiceData from './src/data/invoiceData.js';
const app = express();

app.get('/generate-invoice', async (req, res) => {
  try {
    const invoiceElement = React.createElement(Invoice, { data: invoiceData });
    const pdfStream = await renderToStream(invoiceElement);
    res.setHeader('Content-Type', 'application/pdf');
    pdfStream.pipe(res);
  } catch (error) {
    console.error('Error generating PDF:', error);
    res.status(500).send('Error generating PDF');
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Error Handling and Troubleshooting Common Issues with React-PDF

Common issues you might encounter with React-PDF include:

  • Font Rendering Problems: Ensure fonts are correctly registered and accessible. Use supported font formats like WOFF or TTF.

  • Styling Limitations: React-PDF does not support all CSS properties. Refer to the documentation for supported styles and utilize Flexbox for layout control.

  • Image Embedding Errors: Use absolute URLs or properly import images to ensure they render in the PDF.

  • Large PDF Sizes: Optimize images by compressing them and include only necessary fonts to reduce file size.

Utilize React-PDF’s API and error messages for effective debugging. Implement try-catch blocks and console logging to handle exceptions gracefully.

How to Use a PDF API to Automate PDF Creation at Scale

For SaaS platforms, automating PDF generation at scale might require offloading the heavy lifting to a PDF API.

It's also an option to integrate with third-party APIs like pdforge you can handle high-volume PDF generation, complex formatting, and post-processing, all from a single backend call.

Here’s an example of how to integrate pdforge in Rails to convert HTML content into a PDF via an API call:

fetch('https://api.pdforge.com/v1/pdf/sync', {
  method: 'POST',
  body: JSON.stringify({ templateId: 'your-template' , data: {html:'your-html' } }),
  headers: { 'Authorization' : 'Bearer your-api-key' }
});

This code sends a POST request to the pdforge API, receives the generated PDF, and saves it locally.

Conclusion

React-PDF offers a powerful solution for generating PDF reports from HTML in React applications. Its integration with PDFKit and use of React components make it an excellent choice for developers seeking a declarative and intuitive approach.

However, React-PDF may not suit all scenarios. If you require low-level control over PDF structures or need to convert complex HTML directly, alternatives like pdf-lib or browser-oriented libraries like Puppeteer and Playwright might be more appropriate.

For large-scale applications demanding advanced features and scalability, leveraging third-party PDF APIs such as pdforge is recommended. These services handle the complexities of PDF generation, allowing you to focus on your application’s core functionality.

Generating pdfs at scale can be quite complicated!

Generating pdfs at scale can be quite complicated!

We take care of all of this, so you focus on what trully matters on your Product!

We take care of all of this, so you focus on what trully matters on your Product!

Try for free

7-day free trial

Table of contents

Title