How to Generate Custom PDF, Using React and React-PDF.
Tags: react, javascript, css, frontend
Published: 2023-04-25 20:58:48

## Introduction

Generating a PDF from a webpage is becoming essential in web development, especially in a report-driven application. It is often used in scenarios ranging from creating an invoice in an e-commerce application, printing transactional reports, generating reports, etc. In this article, we will explore how to create an application that generates, prints, downloads, and shares a PDF with [ReactJS](https://react.dev/) and [React-PDF](https://www.npmjs.com/package/react-pdf). 

You can also check out [eduproject](https://eduprojecttopics.com/design-and-implementation-of-custom-pdf-using-react-and-react-pdf/), explaining the design and implementation of custom PDF using React and React-pdf.

Together, we will go through step by step procedure I use to design an e-commerce invoice with the `react-pdf` package. 

### Page preview
The image below shows the webpage preview displaying download, print, and share buttons on every invoice.

![project preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tjnxn8fdkhbyo6cekrby.png)


### PDF Invoice design
The image below shows the screenshot of a sample PDF invoice we will design for this project.

![Invoice design](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lz6xvpp97tgzoi2lo2nn.png)


### Page in action
The image below shows a preview of this project. Each invoice sample has `download`, `print`, and `share` buttons.


![Project demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1srl2gfk22q0yny8q46v.gif)


## Prerequisites

To follow along, we will need the following:

- Basic knowledge of JavaScript and ReactJS.
- Knowledge of CSS Flexbox property.
- The source code of the project is available on [GitHub](https://github.com/jaymeeu/pdf-gen).

## Project Setup.
- To get started with React. Run the below command in the terminal to create a new React project.
   ``` npx create-react-app pdf-gen```

- Change the directory to the project folder by running.
    ```cd pdf-gen```


- Next, let's install the required dependencies by running the below command.
   ``` npm install @react-pdf/renderer file-saver react-icons```


- Next, let’s create these files `Invoice.js` and `PdfCard.js`, we will be adding our code to them as we go along in this guide.


If using a Mac OS, let’s run the below command to create these files

   ```touch src/Invoice.js touch src/PdfCard.js ```

If using a Windows OS, let’s run the below command to create the files, make sure to run the command one after the other.

   ```cd.> src/Invoice.js```
   ```cd.> src/PdfCard.js```



- Then run the `npm start` to start the project. We should have a running react project ready to be modified.


## Project Folder Structure

After the above setup process is completed, we should have our folder structure as below.

![](https://paper-attachments.dropboxusercontent.com/s_1D1CE727DD05861D1795E85D88FF809D7BEC9665770E36086D8D3BE201833E8C_1681642098222_Screenshot+2023-04-16+at+11.47.45.png)


Basically, we will be working with the `App.js` file which is our root file, the `Invoice.js,` and `PdfCard.js`.

The `react-pdf` package will be used to design an e-commerce invoice sample. Before diving in properly, let’s work through the building block of the `react-pdf` package.

## React-PDF

React-PDF is an open-source library for rendering PDF documents in a React application. It is a powerful and flexible library for working with PDF documents in React, making it an excellent choice for building PDF-related features in our projects.

### React-PDF Building Blocks.

- **PDFViewer**: This component will display the PDF documents within the web browser.
- **Document**: This component represents the PDF document and is the parent component to other components.
- **Page**: As the name implies, it represents a single page within a PDF document.
- **View**: This is the core component for designing the UI.
- **Text**: This component is used for displaying text
- **Image**: This component displays images within the document. Support image formats such as JPG, PNG, GIF, etc.
- **PDFDownloadLink**: This component creates a download link for a PDF document generated by the React-pdf library.
- **BlobProvider**: This component generates a PDF document and provides it as a Blob object that can be used to display or download the PDF document.

With this out of the way, let's create our PDF.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtr8wuvfhd5hptnelh3n.gif)

## Design an E-commerce PDF Invoice

In our project folder, let's create a new file called `Invoice.js` in the `src` folder and add code as shown below.

``` jsx 
import React from 'react'
    import { Image, Text, View, Page, Document, StyleSheet } from '@react-pdf/renderer';
    
    const Invoice = () => {
    
    const reciept_data = {
    // update reciept_data here
    }
    
    const styles = StyleSheet.create({
    // update Invoice styles here 
    }}
    
    const InvoiceTitle = () => (
    // update InvoiceTitle component here 
    );
    
    const Address = () => (
    // update Address component here
    );
    
    const UserAddress = () => (
    // update UserAddress component here
    );
    
    const TableHead = () => (
    // update TableHead component here
    );
    
    const TableBody = () => (
    // update TableBody component here
    );
    
    const TableTotal = () => (
    // update TableTotal component here
    );
    
      return (
            <Document>
                <Page size="A4" style={styles.page}>
                    <InvoiceTitle  />
                    <Address/>
                    <UserAddress/>
                    <TableHead/>
                    <TableBody/>
                    <TableTotal/>
                </Page>
            </Document>
      )
    }
    export default Invoice
```

The above code snippet shows the `Invoice` component which includes other sub-components which are `InvoiceTitle`,  `Address`,  `UserAddress`, `TableHead`, `TableBody`, and the `TableTotal` components, these components will be updated accordingly as we go along in this guide. 

![](https://paper-attachments.dropboxusercontent.com/s_1D1CE727DD05861D1795E85D88FF809D7BEC9665770E36086D8D3BE201833E8C_1681641698221_Screenshot+2023-04-16+at+11.39.54.png)

The above image shows the section and position of each component in the Invoice design sample.

Open the `App.js` file and replace the existing code with code  as below:

```jsx
 import { PDFViewer } from "@react-pdf/renderer";
    import Invoice from "./Invoice";
    
    function App() {
      return (
        <div>
          <PDFViewer width="1000" height="650" className="app" >
            <Invoice />
          </PDFViewer>
        </div>
      );
    }
    
    export default App;
```

{% gist
https://gist.github.com/jaymeeu/b8cc780d96ad78efa84b97317b693744
%}

Next, let's update invoice styles as below. This includes the stylesheet for styling and beautifying our invoice

{% gist
https://gist.github.com/jaymeeu/f31665dab2357e63397e51834580afe0
%}

Next, let's update `InvoiceTitle` component and make sure to import a logo for the `Image`.

```jsx 
const InvoiceTitle = () => (
            <View style={styles.titleContainer}>
                <View style={styles.spaceBetween}>
                    <Image style={styles.logo} src={logo} />
                    <Text style={styles.reportTitle}>Xpress Enterprises</Text>
                </View>
            </View>
        );
```

Next, let's update `Address` component.

```jsx
const Address = () => (
            <View style={styles.titleContainer}>
                <View style={styles.spaceBetween}>
                    <View>
                        <Text style={styles.invoice}>Invoice </Text>
                        <Text style={styles.invoiceNumber}>Invoice number: {reciept_data.invoice_no} </Text>
                    </View>
                    <View>
                        <Text style={styles.addressTitle}>7, Ademola Odede, </Text>
                        <Text style={styles.addressTitle}>Ikeja,</Text>
                        <Text style={styles.addressTitle}>Lagos, Nigeria.</Text>
                    </View>
                </View>
            </View>
        );
```

Next, let's update `UserAddress` component.

```jsx
const UserAddress = () => (
            <View style={styles.titleContainer}>
                <View style={styles.spaceBetween}>
                    <View style={{maxWidth : 200}}>
                        <Text style={styles.addressTitle}>Bill to </Text>
                        <Text style={styles.address}>
                            {reciept_data.address}
                        </Text>
                    </View>
                    <Text style={styles.addressTitle}>{reciept_data.date}</Text>
                </View>
            </View>
        );
```

Next, let's update `TableHead` component.

```jsx
 const TableHead = () => (
            <View style={{ width:'100%', flexDirection :'row', marginTop:10}}>
                <View style={[styles.theader, styles.theader2]}>
                    <Text >Items</Text>   
                </View>
                <View style={styles.theader}>
                    <Text>Price</Text>   
                </View>
                <View style={styles.theader}>
                    <Text>Qty</Text>   
                </View>
                <View style={styles.theader}>
                    <Text>Amount</Text>   
                </View>
            </View>
        );
```

Next, let's update `TableBody` component.

```jsx
const TableBody = () => (
           reciept_data.items.map((receipt)=>(
            <Fragment key={receipt.id}>
                <View style={{ width:'100%', flexDirection :'row'}}>
                    <View style={[styles.tbody, styles.tbody2]}>
                        <Text >{receipt.desc}</Text>   
                    </View>
                    <View style={styles.tbody}>
                        <Text>{receipt.price} </Text>   
                    </View>
                    <View style={styles.tbody}>
                        <Text>{receipt.qty}</Text>   
                    </View>
                    <View style={styles.tbody}>
                        <Text>{(receipt.price * receipt.qty).toFixed(2)}</Text>   
                    </View>
                </View>
            </Fragment>
           ))
        );
```

Next, let's update `TableTotal` component.

```jsx
const TableTotal = () => (
            <View style={{ width:'100%', flexDirection :'row'}}>
                <View style={styles.total}>
                    <Text></Text>   
                </View>
                <View style={styles.total}>
                    <Text> </Text>   
                </View>
                <View style={styles.tbody}>
                    <Text>Total</Text>   
                </View>
                <View style={styles.tbody}>
                    <Text>
                        {reciept_data.items.reduce((sum, item)=> sum + (item.price * item.qty), 0)}
                    </Text>  
                </View>
            </View>
        );
```
Once all components and pages are updated, we should see our PDF displayed on the screen as below.

![let get started](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0vpup1afgvapcvhj3tn.png)


Now we have a PDF Invoice file that can be downloaded, printed, and shared via email.
In a real-world scenario, it is likely we have an Invoice or list of invoices requiring actions such as downloading, printing, and sharing via email. Using some custom hooks from React-PDF, we can implement these features. Let's update our design to accommodate these action buttons (download, print, and share).

Let's create a new file `PdfCard.js` update as below:

```jsx
import React from 'react'
    import {CgFileDocument} from 'react-icons/cg'
    import {HiOutlineDownload, HiOutlinePrinter} from 'react-icons/hi'
    import {FiShare2} from 'react-icons/fi'
    
    const PdfCard = ({title}) => {
    
        const styles = {
            container : {  width:'220px',  borderRadius : '5px',  padding:'15px 12px',  display:'flex',  flexDirection:'column',  gap:'15px',  boxShadow: "0 3px 10px rgb(0 0 0 / 0.2)"},
            flex : { width:'100%', display:'flex', gap:'5px', alignItems:'center' },
            bold : { fontSize:'13px', fontWeight: 600},
            thin : {  fontSize:'11px',  color:'#6f6f6f',  fontWeight: 500 },
            btn:{ borderRadius : '3px', border:'1px solid gray', display : 'flex', alignItems :'center', gap:'2px', padding : '3px', fontSize:'11px', color:'#4f4f4f', fontWeight: 600, cursor:'pointer', userSelect:'none'}
        }
    
      return (
        <div style={styles.container}>
            <div style={styles.flex}>
                <CgFileDocument color='#90e0ef' size={20}/>
                <span style={styles.bold}>{title}</span>
            </div>
            <div style={styles.thin}>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Ducimus eligendi reiciendis fuga doloremque
            </div>
    
            <div style={{...styles.flex, ...{justifyContent:'space-between'}}}>
                <div style={styles.btn}>
                    <HiOutlineDownload size={14}/>
                    <span>Download</span>
                </div>
    
                <div style={styles.btn}>
                    <HiOutlinePrinter size={14}/>
                    <span>Print</span>
                </div>
                <div style={styles.btn}>
                    <FiShare2 size={14}/>
                    <span>Share</span>
                </div>
            </div>
    
    
        </div>
      )
    }
    
    export default PdfCard
```

To see this design in action, let's update the `App.js` as below:

```jsx
 import PdfCard from "./PdfCard";
    
    function App() {
      const cards = {  maxWidth: "1200px", margin: "0 auto", display: "grid", gap: "1rem", padding : '20px', gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))"}
      return (
        <div>
          <h2 style={{textAlign:'center'}}>List of invoices</h2>
          <div style={cards}>
            <PdfCard title="Oasic ltd Invoice"/>
            <PdfCard title="Libra ltd Invoice"/>
            <PdfCard title="Xpress ltd Invoice"/>
            <PdfCard title="Cardic ltd Invoice"/>
          </div>
        </div>
      );
    }
    
    export default App;
```

We should see the webpage below:


![page preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vbxirk7v1uqo974cvx8s.png)


Next, let's implement the actions button functionalities.

### Downloading PDF Invoice with React-PDF

To download PDF we will be using a custom hook `PDFDownloadLink` from `React-PDF`. This component creates a download link for a PDF document generated by the react-pdf library.
Let's update our `PdfCard.js` with the `PDFDownloadLink` as below:

```jsx
<PDFDownloadLink document={<Invoice />} fileName='invoice.pdf'>
       <div style={styles.btn}>
          <HiOutlineDownload size={14}/>
          <span>Download</span>
       </div>
     </PDFDownloadLink>
```

Ensure to import `PDFDownloadLink` from `@react-pdf/renderer` and `Invoice` from `Invoice.js`. Once updated, click the download button and see the invoice download to the localdisk of the computer. With that done, let's move on to the `Print` functionality.

### Printing PDF Invoice with React-PDF

To print PDFs, we will use a custom hook `BlobProvider` from `React-PDF.` This component generates a PDF document and provides it as a Blob object that can be used to display or download the PDF document.

Let's update our `PdfCard.js` with the `BlobProvider` as below:

```jsx
<BlobProvider document={<Invoice />}>
      {({ url, blob }) => (
         <a href={url} target="_blank" style={styles.btn}>
            <HiOutlinePrinter size={14}/>
               <span>Print</span>
         </a>
      )}
     </BlobProvider>
```

Ensure to import `BlobProvider` from `@react-pdf/renderer`. Once updated, click the print button and see the invoice preview in a new tab. And finally, let's implement the `Share` functionality.

### Share PDF Invoice with React-PDF

To share PDFs via email, we will use a custom hook, `BlobProvider` from `React-PDF`. This implementation will download a PDF file to the computer's local disk and open a mailbox for us to attach the downloaded file.

To see this in action, let's update our `PdfCard.js` as below:

```jsx 
import { saveAs } from "file-saver";
    
    const handleShare = async (blob) => {
            await saveAs(blob, `invoice.pdf`);
            window.location.href = `mailto:?subject=${encodeURIComponent(`Invoice`)}&body=${encodeURIComponent(`Kindly find attached invoice`)}`;
        }
    ...
    ...
    ...
    
     <BlobProvider document={<Invoice />}>
       {({ url, blob }) => (
         <div style={styles.btn} onClick={() => handleShare(url, blob)} >
            <FiShare2 size={14} />
            <span>Share</span>
         </div>
       )}
    </BlobProvider>

```

## Conclusion

In this article, we built an application that generates, prints, downloads, and shares a PDF with ReactJS and React-PDF.

To learn more about React-PDF, check out its official [documentation](https://react-pdf.org/).

If this article is helpful, consider liking, sharing, and following me; connect with me on [Twitter](https://twitter.com/Abdulrasaq_Jay) for more articles like this.

Happy coding.