Quickstart
Next.js
Prerequisites
- A PDF4.dev account — sign up free
- An API key from Settings stored in
.env.localasPDF4_API_KEY - A template created in the dashboard
Option A: API route (App Router)
Create a route handler that generates and streams the PDF back to the client.
import { type NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const { invoice_number, company_name, total } = await request.json();
const response = await fetch("https://pdf4.dev/api/v1/render", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PDF4_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
template_id: "invoice",
data: { invoice_number, company_name, total },
}),
});
if (!response.ok) {
const error = await response.json();
return NextResponse.json({ error: error.error.message }, { status: response.status });
}
const pdf = await response.arrayBuffer();
return new NextResponse(pdf, {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": `attachment; filename="invoice-${invoice_number}.pdf"`,
},
});
}Call it from the client:
"use client";
export function DownloadInvoiceButton({ invoiceNumber }: { invoiceNumber: string }) {
async function handleDownload() {
const response = await fetch("/api/invoice", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
invoice_number: invoiceNumber,
company_name: "Acme Corp",
total: "$4,500.00",
}),
});
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `invoice-${invoiceNumber}.pdf`;
a.click();
URL.revokeObjectURL(url);
}
return <button onClick={handleDownload}>Download PDF</button>;
}Option B: Server action
Generate and save a PDF directly from a server action.
"use server";
import { writeFile } from "node:fs/promises";
export async function generateInvoicePdf(data: {
invoice_number: string;
company_name: string;
total: string;
}) {
const response = await fetch("https://pdf4.dev/api/v1/render", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PDF4_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ template_id: "invoice", data }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
const buffer = Buffer.from(await response.arrayBuffer());
await writeFile(`/tmp/invoice-${data.invoice_number}.pdf`, buffer);
return { path: `/tmp/invoice-${data.invoice_number}.pdf` };
}Environment variables
PDF4_API_KEY=p4_live_your_key_hereThe API key is never exposed to the client — all requests to PDF4.dev are made server-side.
Next steps
- PDF format — customize page size and margins
- Templates — manage templates via API
- API reference — full render endpoint docs