By Rion Martin
·
Last updated on

How to Generate PDFs with NodeJS and React PDF

A quick tutorial for building a barebones NodeJS API that will generate PDFs.

Cover Image for How to Generate PDFs with NodeJS and React PDF

In this tutorial, we'll use NodeJS, Express and React PDF to generate PDFs from POST requests to an endpoint.

We'll also be using TypeScript to make it easy to work with React on the backend. The repo for this tutorial can be found here: Sample React PDF API.

If you just want a quick way to generate PDFs, check out Export SDK. It provides a visual template builder, REST API, Javascript SDK and generous free plan.

Otherwise, let's get started.

Directory structure

The app will consist of just four files.

pdf-generation-api
│   package.json
│   tsconfig.json
└───src
    │   index.ts
    │   create-template.tsx

Step 1 - Set up the project

Create a source directory and configure a NodeJS project with Typescript.

mkdir pdf-generation-api && cd pdf-generation-api
npm i --save-dev typescript ts-node nodemon

Next, install these three dependencies.

npm i @react-pdf/renderer express react

Now, configure tsconfig.json for TypeScript.

tsconfig.json
{
    "compilerOptions": {
      "jsx": "react",
      "module": "commonjs",
      "target": "es6",
      "rootDir": "./src",
      "esModuleInterop": true,
      "outDir": "./build"
    }
}

Finally, set up some scripts in package.json to test and build the project.

package.json
"scripts": {
  "serve": "nodemon src/index.ts",
  "build": "tsc --build",
  "serve:build": "node build/index.js"
}

Step 2 - Configure Express

Create an index file with a basic Express configuration to serve our project.

mkdir src && cd src && touch index.ts

Open the index.ts file and add the following code.

index.ts
import express from "express";
const app = express();
app.use(express.json());
const port = 3000;
app.listen(port, () => {
  console.log(`The sample PDF app is running on port ${port}.`);
});

Note: To parse JSON objects from our POST requests, we configure Express to use its express.json() function.

Step 3 - Build a template

Next, we'll create a file with a template and a function to generate our PDF (make sure you’re still in the src folder).

touch create-template.tsx

Open create-template.tsx and add the following.

create-template.tsx
import React from "react";
import ReactPDF, {
  Page,
  Text,
  View,
  Document,
  StyleSheet,
} from "@react-pdf/renderer";

The named exports (Page, Text, etc. ) we’re importing from React-PDF are components we can use to structure our PDF documents.

You can find a full list of available components here: React PDF Components.

Before we go any further, we should define what we’re going to render on our PDF. To do this, let’s declare a type for the expected object.

create-template.tsx
type TemplateData = {
  firstName: string;
  lastName: string;
  description: string;
  sections: string[];
  company: string;
  address: string;
  cost: string;
};

The type will be used in an interface for the props that will be passed to our component.

create-template.tsx
interface PDFProps {
  data: TemplateData;
}

Now, let's add some basic styling using the React PDF Stylesheet method.

This method expects a valid JS object as the sole argument and returns an object that you can pass down to components via the style prop.

create-template.tsx
const styles = StyleSheet.create({
  page: {
    backgroundColor: "#E4E4E4",
  },
  section: {
    margin: 10,
    padding: 10,
  },
  heading: {
    fontSize: 24,
    fontWeight: 600,
    color: "#131925",
    marginBottom: 8
  },
  statement: {
    fontSize: 20,
    color: "#131925",
    lineHeight: 1.4,
    marginBottom: 4,
  },
  divider: {
    width: "100%",
    height: 1,
    backgroundColor: "#999999",
    margin: "24px 0 24px 0"
  },
  paragraph: {
    fontSize: 12,
    color: "#212935",
    lineHeight: 1.67,
  },
  columnParent: {
    flexDirection: "row",
    justifyContent: "space-between"
  },
  columnStart: {
    flex: 1,
  },
  columnEnd: {
    flex: 1,
    alignItems: "flex-end"
  },
});

Finally, create the PDF component.

create-template.tsx
const PDF = ({ data }: PDFProps) => {
  return (
    <Document>
      <Page size="A4" style={styles.page}>
        <View style={styles.section}>
          <View style={styles.columnParent}>
            <View style={styles.columnStart}>
              <Text style={styles.heading}>{data.companyName}</Text>
              <Text style={styles.paragraph}>{data.companyPhone}</Text>
              <Text style={styles.paragraph}>{data.companyEmail}</Text>
            </View>
            <View style={styles.columnEnd}>
              <Text style={styles.heading}>Receipt</Text>
              <Text style={styles.paragraph}>Receipt number: {data.receiptNumber}</Text>
              <Text style={styles.paragraph}>Date paid: {data.datePaid}</Text>
              <Text style={styles.paragraph}>Payment method: {data.paymentMethod}</Text>
            </View>
          </View>
          <View style={styles.divider}></View>
          <View>
            <Text style={styles.statement}>{`${data.amount} paid on ${data.datePaid}`}</Text>
            <Text style={styles.paragraph}>Thank you for your business!</Text>
          </View>
        </View>
      </Page>
    </Document>
  );
};

Note on styling: Styling is achieved by passing styles from our styles object to the style attribute of each component.

Step 4 - Create a rendering function

To render PDFs, we'll create an asynchronous function that calls the React-PDF renderToStream helper function with the data and template.

create-template.tsx
export default async (data: TemplateData) => {
  return await ReactPDF.renderToStream(<PDF {...{ data }} />);
};

The above receives the data payload, passes it as props to our PDF component, generates the PDF with the renderToStream helper, and returns a fully rendered PDF file.

Click to see the full create-template.tsx file.

Step 5 - Import rendering function into index.ts

Now that we've created a function to render PDFs, we need to import it into our index.ts so that we can use it for API requests.

Add the following to the top of index.ts.

index.ts
import createTemplate from "./create-template";

Step 6 - Configure an endpoint to receive requests

The last step is setting up an endpoint. The function below receives our data, calls our createTemplate function, and returns a PDF (modify to fit your needs).

Add the following to index.ts below the imports and above the line starting with app.listen.

index.ts
app.post("/", async (req, res) => {
    // Calling the template render func with dynamic data
    const result = await createTemplate(req.body);
  
    // Setting up the response headers
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `attachment; filename=export.pdf`);
  
    // Streaming our resulting pdf back to the user
    result.pipe(res);
});

Click to see the full index.ts file.

Step 7 - Test your project

You can use an API client like Postman to test your app.

But before we test, we need to launch our dev server.

npm serve

Now, we can send a POST request with a JSON object for the body to our server at localhost:3000.

Here's our JSON blob.

{
    "companyName": "Blank Labs",
    "companyPhone": "555-555-5555",
    "companyEmail": "[email protected]",
    "receiptNumber": "101445",
    "datePaid": "1/4/2022",
    "paymentMethod": "Visa",
    "amount": "$29.99"
}

And here's what the POST request looks like in Postman.

Example POST request in Postman setting body as JSON

To view the PDF, save the response as a file.

Highlight of how to save Postman response as a file

That’s it! You now have a simple NodeJS project that can generate PDFs.

Export SDK: The faster way to generate PDFs

Export SDK helps you easily design and generate PDFs with a visual template builder, REST API, Javascript SDK, and no-code integrations.

Create a free account and generate up to 250 pages per month.

Get started for free