DEV Community

Cover image for Generating Dynamic PDFs with React. A Step-by-Step Guide.
Kartik Budhraja
Kartik Budhraja

Posted on

Generating Dynamic PDFs with React. A Step-by-Step Guide.

Overview -

In this article, we will explore the dynamic world of PDF generation with react-pdf in React. From the basics of the library to viewing generated PDFs in a browser using PDFViewer, you will gain insights into creating secure and performance-oriented solutions. Let’s dive into the realm of dynamic PDFs with react-pdf.

Lets assume that you own a e-commerce business. In such a place, you would need to create invoices, reports, or any other printable content for your customers.

The one way is to manually add the data inside the pdf and then send it to the user.

But what if we have hundreds of customers and we have to generate hundreds of PDFs in a single day? The process of manually generating invoices or reports for each order can quickly become overwhelming, consuming valuable time and energy. So, what is the solution?

Enter react-pdf, a game-changer in the world of document generation. It offers an elegant solution for automating the creation of PDF documents within the React environment, making your life as an online business owner significantly easier.

What is React-PDF/Renderer?

React-pdf/Renderer exports a set of React primitives that enable you to render things into your document very easily. It also has an API for styling them, using CSS properties and Flexbox layout.

Installation -
Before diving into the code, you need to install react-pdf/renderer. Make sure you have a React project set up. If not, you can create one using create-react-app. Once your project is ready, follow these steps:

  • Install react-pdf/renderer:

Open your terminal and run the following command to add ‘react-pdf/renderer’ to your project:

with npm:

npm install @react-pdf/renderer

or with Yarn:

yarn add @react-pdf/renderer

  • Import react-pdf/renderer:
import React from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
Enter fullscreen mode Exit fullscreen mode

Creating a Simple PDF -

Once you’ve installed react-pdf/renderer, time to create a simple PDF document.

import React from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';

const styles = StyleSheet.create({
  page: {
    flexDirection: 'row',
    backgroundColor: '#E4E4E4',
  },
  section: {
    margin: 10,
    padding: 10,
    flexGrow: 1,
  },
});

const MyPDF = () => {
  return (
    <Document>
      <Page size="A4" style={styles.page}>
        <View style={styles.section}>
          <Text>Hello, React-PDF!</Text>
         <Text>{`const greet = "Hello, World!";`}</Text>
        </View>
      </Page>
    </Document>
  );
};

export default MyPDF;
Enter fullscreen mode Exit fullscreen mode

<Document> : The root component for your PDF document. It acts as a container for one or more Page components.

<Page> : Represents a page within the PDF document. You can specify its size (e.g., “A4”) and add content to it.

<View> : A container component that can be used to group and style other components.

<Text> : A component for displaying text content within the PDF.

<Image> : Used to display network or local(Node only) JPG or PNG images into a PDF.

<PDFDownloadLink> : This is an anchor tag to enable generating and downloading PDF documents.

Generating the PDF -

Now, let’s render the PDF to a file or display it in your React application. You can do this by using the PDFViewer component from @react-pdf/renderer.

import React from 'react';
import { PDFViewer } from '@react-pdf/renderer';
import MyPDF from './MyPDF';

const App = () => {
  return (
    <div>
      <PDFViewer style={{ width: '100%', height: '500px' }}>
        <MyPDF />
      </PDFViewer>
    </div>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

// Without showToolbar={false}

Image description

** We can also remove the ToolBar using showToolbar={false} in PDFViewer.

Below is the code -

<PDFViewer style={{ width: "100%", height: "800px" }} showToolbar={false}>
  <MyPDF />
</PDFViewer>

Enter fullscreen mode Exit fullscreen mode

// With showToolbar={false}

Image description

** We can even change/reduce the viewer’s available space

const styles = StyleSheet.create({
  viewer: {
    width: window.innerWidth / 3,
    height: window.innerHeight / 2,
  },
});

Enter fullscreen mode Exit fullscreen mode

Now let us add the data from the Invoice Data File -

Create a folder within the src folder and name it InvoiceData. Within the InvoiceData folder, create a new file and name it InvoiceData.js.

const InvoiceData = {
  id: "5df3180a09ea1",
  invoice_no: "873512-28",
  fullname: "John Doe",
  email: "john@gmail.com",
  phone: "+91 777-9999",
  address: "lorem ipsum",
  trans_date: "17-08-2022",
  companyID: "10001",
  companyName: "abc company",
  items: [
    {
      sno: 1,
      desc: "FinePix Pro2 3D Camera",
      qty: 2,
      rate: 1600.0
    },
    {
      sno: 2,
      desc: "Luxury Ultra thin Wrist Watch",
      qty: 1,
      rate: 300.99
    }
  ]
};

export default InvoiceData;
Enter fullscreen mode Exit fullscreen mode

1. Let’s create Invoice main component -

Create a folder within the src folder and name it Invoice. Within the Invoice folder, create a new file and name it InvoiceComponent.js.

import React from "react";
import { StyleSheet, Image, Text, View } from "@react-pdf/renderer";
import InvoiceNo from "./InvoiceNo";
import InvoiceBillTo from "./InvoiceBillTo";
import InvoiceItemsTable from "./InvoiceItemsTable";

const styles = StyleSheet.create({
  page: {
    backgroundColor: "#fff",
    fontFamily: "Helvetica",
    fontSize: 11,
    paddingTop: 30,
    paddingLeft: 50,
    paddingRight: 50,
    lineHeight: 1.5,
    flexDirection: "column"
  },
  logo: {
    width: 84,
    height: 70
  },
  mainHeader: {
    display: "flex",
    flexDirection: "row-reverse",
    justifyContent: "space-between",
    alignItems: "center"
  }
});

const InvoiceComponent = ({ invoice }) => {
  return (
    <View>
      <View style={styles.mainHeader}>
        <Image
          style={styles.logo}
          src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/2300px-React-icon.svg.png"
        />
        <Text>Hello, {invoice.fullname}!</Text>
      </View>
      <InvoiceNo invoice={invoice} />
      <InvoiceBillTo invoice={invoice} />
      <InvoiceItemsTable invoice={invoice} />
    </View>
  );
};

export default InvoiceComponent;
Enter fullscreen mode Exit fullscreen mode

2. Invoice Number Component -

Create InvoiceNo.js within the Invoice folder and add the following code:

import React from "react";
import { Text, View, StyleSheet } from "@react-pdf/renderer";

const styles = StyleSheet.create({
  invoiceNoContainer: {
    flexDirection: "row",
    marginTop: 36,
    justifyContent: "flex-end"
  },
  invoiceDateContainer: {
    flexDirection: "row",
    justifyContent: "flex-end"
  },
  invoiceInvoiceNo: {
    fontSize: 12,
    fontStyle: "bold"
  },
  invoiceDate: {
    fontSize: 12,
    fontStyle: "bold"
  },
  label: {
    marginRight: 15
  }
});

const InvoiceNo = ({ invoice }) => (
  <>
    <View style={styles.invoiceNoContainer}>
      <Text style={styles.label}>Invoice No:</Text>
      <Text style={styles.invoiceInvoiceNo}>{invoice.invoice_no}</Text>
    </View>
    <View style={styles.invoiceDateContainer}>
      <Text style={styles.label}>Date: </Text>
      <Text style={styles.invoiceDate}>{invoice.trans_date}</Text>
    </View>
  </>
);

export default InvoiceNo;
Enter fullscreen mode Exit fullscreen mode

3. Invoice BillTo Component -

Create InvoiceBillTo.js within the Invoice folder and add the following code:

import React from "react";
import { Text, View, StyleSheet } from "@react-pdf/renderer";

const styles = StyleSheet.create({
  headerContainer: {
    marginTop: 36,
    justifyContent: "flex-start",
    width: "50%"
  },
  billTo: {
    marginRight: 10
  },
  Mainbillto: {
    display: "flex",
    flexDirection: "row",
    marginTop: 20,
    paddingBottom: 3
  }
});

const InvoiceBillTo = ({ invoice }) => (
  <View style={styles.headerContainer}>
    <View style={styles.Mainbillto}>
      <Text style={styles.billTo}>Bill To:</Text>
      <Text>{invoice.fullname}</Text>
    </View>
    <View style={styles.Mainbillto}>
      <Text style={styles.billTo}>Address:</Text>
      <Text>{invoice.address}</Text>
    </View>
    <View style={styles.Mainbillto}>
      <Text style={styles.billTo}>Phone no:</Text>
      <Text>{invoice.phone}</Text>
    </View>
    <View style={styles.Mainbillto}>
      <Text style={styles.billTo}>Email:</Text>
      <Text>{invoice.email}</Text>
    </View>
  </View>
);

export default InvoiceBillTo;
Enter fullscreen mode Exit fullscreen mode

4. Invoice Items Table Component -

In this component we will create a table with the JSON data.

Create InvoiceItemsTable.js within the Invoice folder and add the following code:

import React from "react";
import { View, StyleSheet, Text } from "@react-pdf/renderer";
import InvoiceTableRow from "./InvoiceTableRow";

const borderColor = "#00519C";

const styles = StyleSheet.create({
  tableContainer: {
    flexDirection: "row",
    flexWrap: "wrap",
    marginTop: 24,
    borderWidth: 1,
    borderColor: "#3778C2"
  },
  container: {
    flexDirection: "row",
    borderBottomColor: "#00519C",
    backgroundColor: "#00519C",
    color: "#fff",
    borderBottomWidth: 1,
    alignItems: "center",
    height: 24,
    textAlign: "center",
    fontStyle: "bold",
    flexGrow: 1
  },
  description: {
    width: "60%",
    borderRightColor: borderColor,
    borderRightWidth: 1
  },
  qty: {
    width: "10%",
    borderRightColor: borderColor,
    borderRightWidth: 1
  },
  rate: {
    width: "15%",
    borderRightColor: borderColor,
    borderRightWidth: 1
  },
  amount: {
    width: "15%"
  }
});

const InvoiceItemsTable = ({ invoice }) => (
  <View style={styles.tableContainer}>
    {/* Invoice Table Header */}
    <View style={styles.container}>
      <Text style={styles.description}>Item Description</Text>
      <Text style={styles.qty}>Qty</Text>
      <Text style={styles.rate}>Price</Text>
      <Text style={styles.amount}>Amount</Text>
    </View>
    {/* Invoice Table Rows */}
    <InvoiceTableRow items={invoice.items} />
  </View>
);

export default InvoiceItemsTable;
Enter fullscreen mode Exit fullscreen mode

5. Invoice Items Data Rows Component -

Create InvoiceTableRow.js within Invoice folder. We need to pass the invoice line items data as a props to this component and using map() function, we will iterate through the line items to create individual table rows.

This below code for InvoiceTableRow.js

import React from "react";
import { Text, View, StyleSheet } from "@react-pdf/renderer";

const borderColor = "#3778C2";
const styles = StyleSheet.create({
  row: {
    flexDirection: "row",
    borderBottomColor: "#3778C2",
    borderBottomWidth: 1,
    alignItems: "center",
    height: 24,
    fontStyle: "bold"
  },
  description: {
    width: "60%",
    textAlign: "left",
    borderRightColor: borderColor,
    borderRightWidth: 1,
    paddingLeft: 8
  },
  qty: {
    width: "10%",
    borderRightColor: borderColor,
    borderRightWidth: 1,
    textAlign: "right",
    paddingRight: 8
  },
  rate: {
    width: "15%",
    borderRightColor: borderColor,
    borderRightWidth: 1,
    textAlign: "right",
    paddingRight: 8
  },
  amount: {
    width: "15%",
    textAlign: "right",
    paddingRight: 8
  }
});

const InvoiceTableRow = ({ items }) => {
  const rows = items.map((item) => (
    <View style={styles.row} key={item.sno.toString()}>
      <Text style={styles.description}>{item.desc}</Text>
      <Text style={styles.qty}>{item.qty}</Text>
      <Text style={styles.rate}>{item.rate}</Text>
      <Text style={styles.amount}>{(item.qty * item.rate).toFixed(2)}</Text>
    </View>
  ));
  return <>{rows}</>;
};

export default InvoiceTableRow;
Enter fullscreen mode Exit fullscreen mode

Here is the result —

Image description

Conclusion -

In this article, we explored the capabilities of react-pdf for dynamic PDF generation and viewing using PDFViewer. We learnt about its fundamental components and its lightweight, performance-oriented nature. Armed with this knowledge, you are ready to create secure and efficient PDF solutions for your projects. Happy coding and happy PDF generation!

Thanks for reading this article❤️. If you found this article useful then hit that like button 👏. Do follow for more! Happy Coding❤️.

Top comments (3)

Collapse
 
jm__solo profile image
Juan Oliú

Awesome article 👏.

Collapse
 
quanscheng profile image
Transon

nice article, could you know how to render "≥" or "≤" ? In fact, I use entity character render ">"(&gt;), "<"(&lt;), but &ge;and &le; are not work.

Collapse
 
ajmalapiacademy profile image
AJMAL

What about Latex? can we integrate Latex ?