Generating Pdf documents in React Using React-pdf
Oghenero Adaware

Oghenero Adaware @finallynero

About: Software Developer. Javascript, React, React-native, Vue and Gatsby

Location:
Nigeria
Joined:
Nov 23, 2017

Generating Pdf documents in React Using React-pdf

Publish Date: Jul 22 '19
216 24

This post was originally published on my Blog, check it out for more contents.

Introduction

I was working on a project recently and I got a unique(for me) requirement that needed me to generate pdf file from an array of values in the Browser, usually in my little experience in software development Pdf files are generated in the backend using Puppeteer for node js and FPDF for PHP e.t.c. So I had to look for a React library that could work for my use case, lucky for me I found React-pdf. I found other libraries like @progress/kendo-react-pdf but I decided to go with React-pdf because of its developer-friendly documentation.
The library was built by Diego Muracciole and also maintained by him.
So in the tutorial/ blog post, I will try to explain briefly how react-pdf works and also walk you through how to generate PDf from an array of objects coming from the Moviedb Api.

Features

While going through the documentation when I was trying to pick the appropriate library for my use-case there were some features of React-pdf that convinced me to use it, I will briefly talk about them:

Components

React-Pdf uses React-Primitives spec to create custom components that you can use to create and structure your PDF documents.
These components include:

  • Document
  • Page
  • View
  • Image
  • Text
  • Link
  • Note
  • Canvas
  • PDFViewer
  • PDFDownloadLink
  • BlobProvider

You can checkout the docs for more details on what each component above does, Basically the components about help you create pdf using JSXesques syntax.

Styling

Now that we have an idea of how to create the PDF document how do we style it? React-pdf provides powerful styling solution using the StyleSheet API which helps you style your document using CSS, Media queries and Flexbox. Check the docs for the CSS properties they support.
What if you are a big fan of CSS-in-JS? well, they also support the entire styled-components API.

Fonts

React-Pdf has a Font API that helps you load fonts from different sources and use in your PDF document.

Those were some of the features that made me pick React-pdf. Also when I checked the Github Repository the maintainer Diego Muracciole is quite active and tries to respond to most issues opened.

Demo

So I will briefly work you through a simple example of generating pdf from the MoviesDB API. This demo is going to demonstrate generating the best movies of the year.

Folder Structure

project
│   package.json
│
│
└───Public
│   │   150.png
│   │   index.html
│   │   star.png
│
│
│
└───src
    │   Movie.jsx
    │   MovieList.jsx
    |   constant.js
    |   index.js
    |   styles.css
Enter fullscreen mode Exit fullscreen mode

index.js(entry)

import React from "react";
import ReactDOM from "react-dom";
import MovieList from "./MovieList";

import "./styles.css";

function App() {
    return (
        <div className="App">
              <MovieList />
        </div>
    );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

The index.js is the entry point of the application. It renders the <MovieList/> which is the parent component of our application.

MovieList.jsx

import React, { useState } from "react";
import Axios from "axios";
import { PDFDownloadLink } from "@react-pdf/renderer";
import { API_KEY } from "./constants";
import { PdfDocument } from "./Movie";

const years = [
  { value: "2010", text: "2010" },
  { value: "2011", text: "2011" },
  { value: "2012", text: "2012" },
  { value: "2013", text: "2013" },
  { value: "2014", text: "2014" },
  { value: "2015", text: "2015" },
  { value: "2016", text: "2016" },
  { value: "2017", text: "2017" },
  { value: "2018", text: "2018" },
  { value: "2019", text: "2019" }
];

export default function MovieList() {
  const [year, setYear] = useState("");
  const [movieDetails, setDetails] = useState([]);
  const [show, setHide] = useState(false)

  const fetchMovie = async e => {
    setYear(e.target.value);
    try {
      let res = await Axios(
        `https://api.themoviedb.org/3/discover/movie?api_key=${API_KEY}&primary_release_year=${year}&sort_by=vote_average.desc`
      );
      setDetails(res.data.results);
      setHide(true)
    } catch (error) {
      console.log(error);
    }
  };

  return (
    <div className="container">
      <h2>Best movies of the year</h2>
      <label htmlFor="movies">Select Year</label>
      <select id="movies" className="select" onChange={fetchMovie}>
        <option defaultValue="" disabled>
          Select your option
        </option>
        {years.map((year, index) => {
          return (
            <option key={index} value={year.value}>
              {year.text}
            </option>
          );
        })}
      </select>
      {show &&<PDFDownloadLink
        document={<PdfDocument data={movieDetails} />}
        fileName="movielist.pdf"
        style={{
          textDecoration: "none",
          padding: "10px",
          color: "#4a4a4a",
          backgroundColor: "#f2f2f2",
          border: "1px solid #4a4a4a"
        }}
      >
        {({ blob, url, loading, error }) =>
          loading ? "Loading document..." : "Download Pdf"
        }
      </PDFDownloadLink>}
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

The MovieList.jsx component contains most of the logic in this application. we import PDFDownloadLink from @react-pdf/renderer, this is basically an anchor tag that enables us to generate and download PDF documents. PDFDownloadLink accepts a document props which are the PDF template we will be creating soon using some of the React-primitives listed early in this post. It also accepts a filename prop that can be used to define the filename of the PDF document, a style prop to add inline styling to the link tag, a className prop if you prefer using classes to style and children prop which is the anchor tag contents.

Movie.jsx

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

const POSTER_PATH = "https://image.tmdb.org/t/p/w154";

const styles = StyleSheet.create({
    page: {
        backgroundColor: "#ffffff"
    },
    section: {
        margin: 10,
        padding: 10,
        flexGrow: 1
    },
    movieContainer: {
        backgroundColor: "#f6f6f5",
        display: "flex",
        flexDirection: "row",
        padding: 5
    },
    movieDetails: {
        display: "flex",
        marginLeft: 5
    },
    movieTitle: {
        fontSize: 15,
        marginBottom: 10
    },
    movieOverview: {
        fontSize: 10
    },

    image: {
        height: 200,
        width: 150
    },
    subtitle: {
        display: "flex",
        justifyContent: "space-between",
        flexDirection: "row",
        width: 150,
        alignItems: "center",
        marginBottom: 12
    },
    vote: {
        display: "flex",
        flexDirection: "row"
    },
    rating: {
        height: 10,
        width: 10
    },
    vote_text: {
        fontSize: 10
    },
    vote_pop: {
        fontSize: 10,
        padding: 2,
        backgroundColor: "#61C74F",
        color: "#fff"
    },
    vote_pop_text: {
        fontSize: 10,
        marginLeft: 4
    },
    overviewContainer: {
        minHeight: 110
    },
    detailsFooter: {
        display: "flex",
        flexDirection: "row"
    },
    lang: {
        fontSize: 8,
        fontWeight: 700
    },
    vote_average: {
        fontSize: 8,
        marginLeft: 4,
        fontWeight: "bold"
    }
});

export function PdfDocument(props) {
    console.log("pdf props", props.data);
    return (
        <Document>
            <Page style={styles.page}>
                {props.data
                    ? props.data.map((a, index) => {
                            return (
                                <View key={index} style={styles.movieContainer}>
                                    <Image
                                        style={styles.image}
                                        source={
                                            a.poster_path !== null
                                                ? `${POSTER_PATH}${a.poster_path}`
                                                : "150.jpg"
                                        }
                                    />
                                    <View style={styles.movieDetails}>
                                        <Text style={styles.movieTitle}>{a.title}</Text>
                                        <View style={styles.subtitle}>
                                            <View style={styles.vote}>
                                                <Image source="star.png" style={styles.rating} />
                                                <Text style={styles.vote_text}>{a.vote_count}</Text>
                                            </View>
                                            <View style={styles.vote}>
                                                <Text style={styles.vote_pop}>{a.popularity}</Text>
                                                <Text style={styles.vote_pop_text}>Popularity</Text>
                                            </View>
                                        </View>
                                        <View style={styles.overviewContainer}>
                                            <Text style={styles.movieOverview}>{a.overview}</Text>
                                        </View>
                                        <View style={styles.detailsFooter}>
                                            <Text style={styles.lang}>
                                                Language: {a.original_language.toUpperCase()}
                                            </Text>
                                            <Text style={styles.vote_average}>
                                                Average Votes: {a.vote_average}
                                            </Text>
                                            <Text style={styles.vote_average}>
                                                Release Date:{" "}
                                                {moment(a.release_date, "YYYY-MM-DD").format(
                                                    " MMMM D Y"
                                                )}
                                            </Text>
                                        </View>
                    </View>
                    </View>
                );
                })
            : ""}
            </Page>
        </Document>
    );
}
Enter fullscreen mode Exit fullscreen mode

This Movie.jsx component is the template of the PDF we are generating, Here we define how the structure of the PDF using React-primitives(VIEW, DOCUMENT) and also style. So I'd briefly talk about the some of React-pdf API's I used here.

  • StyleSheet.create() : It helps you define the styles you want to use in the document, It accepts an object containing all the CSS you want to use in the Document and it returns an object which you can apply to any of the PDF elements via the style prop.

  • Document : The PDFDownloadLink document prop accepts only a component of type Document so this must be the root of your components when creating the PDF template and accepts only child of type Page, The Document is simply a wrapper around your PDF template and it accepts some optional props

  • Page : This indicates a Page in the document and you can have multiple Pages in a document. It accepts some props to define the size of the page, orientation or if you want page wrapping wrap. props

  • View : I will like to compare this component to the HTML div, it helps you section or divide the document. props

  • Text : This component is used for displaying text on the document and applying styles to it. props

  • Image : This component is used for displaying images(network or local) on the document, these images could be PNG, JPG or base64.

Demo Application

Conclusion

Before I used this library I never thought it was possible to generate PDF on the client-side, Not only does react-pdf allow you do that but get to use JSXesque syntax to structure and design the PDF document. I know the demo is quite trivial but I think this library might be useful in some use cases.

Comments 24 total

  • Truong Hoang Dung
    Truong Hoang DungJul 22, 2019

    I tried downloading pdf from your codesandbox but the pdf is empty.
    Is there anything wrong here ?

    • Oghenero Adaware
      Oghenero AdawareJul 22, 2019

      Did you select a year first? I just modified it to hide the download tag until you have selected a year. If it still doesnt work then tell me

      • Truong Hoang Dung
        Truong Hoang DungNov 30, 2019

        I tried to install using webpack (with Razzle.js), but keep getting error:

        Uncaught RangeError: Offset is outside the bounds of the DataView
            at DataView.getUint32 (<anonymous>)
            at new UnicodeTrie (index.js:52)
        

        Do you know how to fix this ?

      • pam8351
        pam8351Feb 11, 2020

        I selected year, but the page still shows "Loading please wait". I also, developed the similar page and seeing the same response when the content is more than 1 page of PDF.

        Can you please help me to resolve this issue?

        return (

            <Document>
                <Page style={styles.page} wrap>
                    <View style={styles.movieDetails}>
                                            <Text style={styles.documentTitle}>Release 20.1 US</Text>
                                            <Text style={styles.documentTitle}>Test Execution detailed report</Text>
                    </View>
                    </Page>
        
                    <Page pageNumber={25} width={600}  wrap>
                    {props.data
                        ? props.data.map((a, index) => {
                                return (
        
        
                                       <View style={styles.TestContainer}>
                                        <View style={styles.movieDetails}>
                                            <Text style={styles.testTitle}>{a.Test_name}</Text>
        
        
                                            </View>
        
                                            <View style={styles.detailsFooter}>
                                            <Text style={styles.testStatus}>Test Status :{a.Test_status}</Text>
                                            <Text style={styles.textLeft}>Test Application :{a.Application}</Text>
                                            <Text style={styles.textLeft}>Testing cycle :{a.Target_cycle}</Text>
                                            </View>
                                            <View style={styles.detailsFooter}>
                                            <Text style={styles.textRight}>Testing Type     :{a.Testing_type}</Text>
                                            <Text style={styles.textRight1}>  Executed by    :{a.Executed_by}</Text>
                                            </View>
                                   </View>
                    );
                    })
                : ""}
                <Text style={styles.pageNumber} render={({ pageNumber, totalPages }) => (
            `${pageNumber} / ${totalPages}`
          )} fixed />
                </Page>
            </Document>
        );
        
  • Stephen Marshall
    Stephen MarshallSep 30, 2019

    Thanks for this! If anyone is curious about a real-life use case, I'm doing the same thing at work right now...

    • Digital mktg team: We need to generate a 35 page PDF SEO report with all the stats!
    • Seo team: OK! Go to our SEO service provider, and get the data you need from their GUI!
    • Digital mktg team: Umm, that's a lot of steps and takes a long time/not very efficient way to generate such a lengthy report! And we need to make a lot them!
    • Business Intelligence (Me): Say no more! I'll build a react app, and make an API call to get the data we need, and generate the report on the fly so you can download it!
  • kirlisakal
    kirlisakalOct 19, 2019

    In this example, I can quickly get PDF doc, however, on my project, I want to get data-table report, and it is too slow... after 5 minutes, I canceled.
    Do you think is there a reason for this?

    Here is my code:

    • Oghenero Adaware
      Oghenero AdawareOct 19, 2019

      Can you a simple demo in codesandbox

      • kirlisakal
        kirlisakalOct 21, 2019

        Hi Nero,
        There are PDF Render codes on my question post, jsFiddle, HTML section.
        fields and records array, and this is simple structure.

        I guess, for each styling property, renderer works too much. When try to minimize styling it renders quicker.

        • Zenobius Jiricek
          Zenobius JiricekMay 12, 2020
          1. you're not include react or react-pdf in that fiddle. ( jsfiddle.net/y2nu9ek7 )
          2. you've just put the jsx in the html section.

          Like Nero says.... if you want help from anyone put your example up on a sandbox that properly supports playing with react.

  • Alexander B.K.
    Alexander B.K.Nov 12, 2019

    I probably have visited React-pdf site several months ago, I am not sure. But then I found pdfmake, I tried it and it met my requirements, so I have been using it until now.
    However, maybe I will give React-Pdf a try.

  • Alexander B.K.
    Alexander B.K.Dec 9, 2019

    I have tried it yesterday. Seeing that the document used JSX, I thought it could contain HTML element. But I was disappointed, because it didn't support HTML element at all. My goal is to use HTML table in this react-pdf library, because my app deals with data records that will be displayed in tabular format, like in HTML table.
    This means I still have to find my own way in order to display my data in tabular format, using only available features in this library : view, text and styles.

    • Zenobius Jiricek
      Zenobius JiricekMay 12, 2020

      You'd have to do something around the use of the Text component and some awesome styling to implement your own table

  • Rahul
    RahulMar 28, 2020

    Thanks for the detailed explanantion, Is there any way i can use input tag as custom components? because when i try i get this error

    Error: Invalid element of type input passed to PDF renderer

    • Zenobius Jiricek
      Zenobius JiricekMay 12, 2020

      you missed this part:

      React-Pdf uses React-Primitives spec to create custom components that you can use to create and structure your PDF documents.

      github.com/lelandrichardson/react-...

      If you read that repo, you'll discover that it's not rendering with the default set of html components that react normally understands.

  • Alex Quasar
    Alex QuasarApr 21, 2020

    I am trying to make this work with Gatsby. Anyone had any luck with this?

  • Muhammad Ali Kazmi
    Muhammad Ali KazmiMay 13, 2020

    I was stuck really bad. You save me big time. Thank you so much !!!

  • Reuven Etzion
    Reuven EtzionJun 2, 2020

    Could not find module in path: '@react-pdf/unicode-properties' relative to '/node_modules/@react-pdf/fontkit/dist/fontkit.browser.es.js'

  • sagar-1987
    sagar-1987Jun 6, 2020

    Hello Nero its a wonderful solution , Thanks for that , I have a requirement where i have a download button in a table rows , so on click of download button of a specific row of table i need make a API call to get the details which need to be displayed in PDF, How can we achieve this using this solution . Thanks . Waiting for the reply :)

  • Siddharth
    SiddharthMay 25, 2021

    I am using a similar logic, but it's making my page unresponsive.
    Can someone help
    this.state.prescription - is the state variable where api response is stored

    {Object.keys(this.state.prescription).length > 0 ? <PDFDownloadLink document={<DownloadPrescription prescription={this.state.prescription} />} fileName="prescription.pdf">
                                <i class="bi bi-download section1_row_icons"></i>
                            </PDFDownloadLink> : null}
    
    Enter fullscreen mode Exit fullscreen mode
  • Alexander Chavers
    Alexander ChaversNov 13, 2021

    What is the proper way to reference the source for images stored as a byte array? I get a PDF to generate just fine, but only with URLs, not using my incoming byte arrays.

    Currently looking at something like this:

  • Venkatesh.R
    Venkatesh.RFeb 28, 2022

    Hi bro im getting HTML from the api response, so how can i print as pdf

  • Gaurav Kumar Sinha
    Gaurav Kumar SinhaOct 12, 2022

    I need one help here. I have been using html2canvas and jspdf to grab the current page and then convert to PDF but I am having unexpected page breaks in middle of image or graphs in the page. Is there a way to implement customized pdf with fixed header and footer in each page and at the same time avoid page breaks like described above. Please let me know if you have delt with such a scenario.

  • Espérant Kela
    Espérant KelaJan 13, 2023

    hello nero

    I am using @react-pdf render for my react application.

    it generated the component in pdf, the problem now is at the level of display of the data coming from my api which are saved in html tags. it displays like this:

    <h1>My Title</h1>
    
    Enter fullscreen mode Exit fullscreen mode

    or I would like it to display without the tags. there is also no way to use dangerouslySetInnerHTML

    please kindly help me. I'm stuck on it

  • kritikgarg
    kritikgargApr 4, 2023

    👏 Thank you for sharing an excellent post, Nero! It's amazing to see how React-pdf simplifies the process of generating PDF documents in React. 👨‍💻 As a developer, I found this very helpful for those who want to incorporate PDF generation into their React applications. 📄💻

    👉 Along with this, I recently came across an informative article on Effortlessly Generate PDF Quotes with Salesforce that provides a complete guide on how to generate PDF quotes with Salesforce. It's a great resource for those who are looking to generate quote PDFs in Salesforce. 📄💻

Add comment