How to Generate PDFs Using Next.js Server Actions
In today's digital landscape, generating PDFs is a common requirement for many applications. Whether it's invoices, reports, contracts, or certificates, PDFs provide a universal format that’s easily shareable and printable. At some point, almost every application needs to dynamically create PDFs based on user data or application logic.
The Need for Dynamic PDFs
PDFs are essential for delivering content in a fixed format, especially when consistency across different devices and platforms is crucial. They are used extensively in industries like finance, healthcare, education, and e-commerce. As applications become more data-driven, the ability to generate PDFs dynamically, populated with user-specific information, becomes a critical feature.
Common Approaches to Programmatically Generate PDFs
There are several ways to generate PDFs programmatically, each with its pros and cons. Here’s a brief overview of the most popular methods:
-
HTML-to-PDF Conversion This method involves designing the PDF content as an HTML page and then converting it to a PDF. Libraries like Puppeteer or wkhtmltopdf are commonly used for this purpose.
Pros:
- Leverages existing web development skills (HTML, CSS).
- Easy to style and format content.
- Supports complex layouts and responsive design.
Cons:
- Can be resource-intensive.
- Requires a headless browser or rendering engine.
- Might involve complex configuration for precise output.
-
PDF Generation Libraries (e.g., jsPDF, PDFKit)
These libraries allow you to create PDFs directly through JavaScript or Node.js code.Pros:
- Full control over PDF content and structure.
- No need for an external rendering engine.
- Suitable for lightweight, simple documents.
Cons:
- Can be time-consuming to format complex layouts.
- Limited support for advanced styling compared to HTML/CSS.
-
Third-Party APIs Services like Printerz (or others) offer APIs for generating PDFs from HTML or other formats.
Pros:
- Simplifies the PDF generation process.
- Offloads the resource-heavy task to a third-party service.
- Often includes features like template management and dynamic data binding.
Cons:
- Dependency on an external service.
- Potential cost associated with usage.
- Limited customization compared to in-house solutions.
Generating Invoice PDFs with Next.js and Printerz
Setting up printerz
- Let's start by creating your printerz account here and grab your API key .
- After that you can grab one of our invoice starter template here (check https://doc.printerz.dev if you want more details) .
Creating a new Next.js app
npx create-next-app@latest nextjs-pdf-server-action --typescript
Create the server action for generating the PDF
'use server'
const invoices = [{
invoiceNumber: "INV-001",
invoiceDate: "2020-01-01",
customer: {
name: "John Doe",
address: "123 Main St, Anytown, USA"
},
invoiceItems: [
{
name: "Acme Product",
quantity: 1,
unitPrice: 10
},
{
name: "Another Product",
quantity: 2,
unitPrice: 20
},
{
name: "Yet Another Product",
quantity: 3,
unitPrice: 30
}
]
}
]
export async function generatePDFInvoice(invoiceId: string) {
const invoice = invoices.find(invoice => invoice.invoiceNumber === invoiceId)
if (!invoice) {
throw new Error(`Invoice ${invoiceId} not found`)
}
const invoiceTemplateId = "02599a55-bc35-4b61-b342-3eb4e8c119ab"
const options = {
method: 'POST',
headers: {
'x-api-key': process.env.PRINTERZ_API_KEY ?? "",
'Content-Type': 'application/json'
},
body: JSON.stringify({
variables: invoice,
options: {
"printBackground": true
}
})
};
const response = await fetch(`https://api.printerz.dev/templates/${invoiceTemplateId}/render`, options);
if (!response.ok) {
throw new Error(response.statusText);
}
const pdfBuffer = await response.arrayBuffer();
return Buffer.from(pdfBuffer)
}
Create a basic page to test the server action
"use client";
import { generatePDFInvoice } from "@/app/actions";
import { useTransition } from "react";
import styles from "./page.module.css";
export default function Home() {
const [isPending, startTransition] = useTransition();
const handleGeneratePDF = () => {
startTransition(async () => {
const pdfBuffer = await generatePDFInvoice("INV-001")
const blob = new Blob([pdfBuffer], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
window.open(url);
})
}
return (
<div className={styles.page}>
<main className={styles.main}>
<button
className={styles.button}
disabled={isPending}
onClick={handleGeneratePDF}
>
{isPending ? "Generating PDF..." : "Generate PDF"}
</button>
</main>
</div>
);
}
Test the server action
After clicking the button we should be redirect to a new tab with the generated PDF.
Conclusion
We have seen how to generate PDFs with Next.js and Printerz. We have also seen how to create a server action to generate the PDF.
For the next step, we can implement the caching of the generated PDF into an object storage service like AWS S3, Google Cloud Storage, or Azure Blob Storage. This will allow us to later serve the PDF directly from the object storage instead of generating it on the server each time the user requests it.
MIT 2025 © Printerz.