DEV Community

Cover image for How to Generate PDF Invoices in a Node.js Backend Using `@react-pdf/renderer`
Ram Kumar Dhimal
Ram Kumar Dhimal

Posted on

How to Generate PDF Invoices in a Node.js Backend Using `@react-pdf/renderer`

Generating PDF invoices on the backend is a common need for many web applications. In this guide, we’ll build a simple way to create PDF invoices using React components with @react-pdf/renderer — but in a Node.js backend environment.

Even if you’re a beginner, this step-by-step explanation will help you understand how to create, render, and send PDFs from your backend server.


Why Use React for PDF Generation?

You might be wondering:

Why would I use React — a frontend UI library — in a Node.js backend?

Good question! Here’s the simple answer:

  • @react-pdf/renderer lets you build PDFs using React components — just like building a UI.
  • This means you describe your invoice as reusable React components with JSX syntax.
  • React helps structure your PDF content clearly and makes it easy to maintain and update.
  • Because @react-pdf/renderer uses React components internally, you need to have react and react-dom installed for it to work correctly.

Why Do We Need react and react-dom in Backend?

  • Even though you’re not building a frontend app, @react-pdf/renderer uses React’s core features to interpret your components and JSX.
  • react is the core library that understands React components.
  • react-dom is often used for DOM-related rendering, and some internal parts of @react-pdf/renderer depend on it, so it must be installed as well.
  • You won’t be using react-dom to render HTML or web pages here — it’s just a required dependency for @react-pdf/renderer.

Step 1: Set Up Your Project and Install Packages

Create a new Node.js project if you don’t have one yet:

mkdir invoice-generator
cd invoice-generator
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install Express (to create the backend API), @react-pdf/renderer for PDF generation, and React dependencies:

npm install express @react-pdf/renderer react react-dom
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a React PDF Invoice Template

Create a file named Invoice.js for your invoice React component.

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

const styles = StyleSheet.create({
  page: { padding: 30 },
  section: { marginBottom: 10 },
});

const Invoice = ({ order }) => (
  <Document>
    <Page style={styles.page}>
      <View style={styles.section}>
        <Text>Invoice for Order: {order._id}</Text>
        {order.products.map((p, i) => (
          <Text key={i}>
            {p.name} — Qty: {p.quantity} — ${p.amount_total}
          </Text>
        ))}
      </View>
    </Page>
  </Document>
);

export default Invoice;
Enter fullscreen mode Exit fullscreen mode

Here, we define a simple invoice that lists the order ID and products with quantities and amounts.


Step 3: Generate PDF in Backend and Send it as Response

In your backend code, you will:

  1. Fetch the order data (from your database).
  2. Render the React PDF component to a PDF stream.
  3. Pipe the PDF stream to the HTTP response so the client receives the PDF file.

Example Express route:

import express from "express";
import ReactPDF from "@react-pdf/renderer";
import Invoice from "./Invoice.js"; // The React component you just created
import { getOrderById } from "./models/orderModel.js"; // Your DB function

const app = express();

app.get("/invoice/:id", async (req, res) => {
  try {
    const id = req.params.id;
    const order = await getOrderById(id);

    if (!order) {
      return res.status(404).send("Order not found");
    }

    // Set headers for PDF response
    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `inline; filename=invoice_${id}.pdf`);

    // Render PDF and pipe to response
    const stream = ReactPDF.renderToStream(<Invoice order={order} />);
    stream.pipe(res);
  } catch (error) {
    console.error(error);
    res.status(500).send("Internal Server Error");
  }
});

app.listen(3000, () => console.log("Server started on port 3000"));
Enter fullscreen mode Exit fullscreen mode

What Does stream.pipe(res) Do?

  • The ReactPDF.renderToStream() method generates a stream of PDF data.
  • Calling stream.pipe(res) sends that PDF data directly to the HTTP response.
  • This way, the client’s browser receives the PDF immediately and can display or download it.

Summary

  • @react-pdf/renderer lets you write PDFs using React components.
  • You need react and react-dom installed because @react-pdf/renderer depends on React’s internals, even in backend.
  • Use Express (or any Node.js server) to create an API endpoint that fetches order data, generates a PDF, and streams it back.
  • The user can access a URL like /invoice/:id to get their invoice as a PDF in the browser.

Some of the things we can do as well:

  • We can customize the Invoice component to add logos, styles, totals, and more.
  • Test with Postman or your browser by visiting http://localhost:3000/invoice/12345 (replace 12345 with a valid order ID).
  • Look at @react-pdf/renderer docs for more components and styling options.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.