SSR & Next.js Support
@easypdf/react is fully SSR-safe. There are no browser globals (window, document, navigator) at import time. All PDF generation code is dynamically imported and guarded — it only runs in the browser, inside a user event handler.
You can import from @easypdf/react in any file, including server components, without errors.
Next.js App Router
No setup is required. Use the hook directly inside any "use client" component.
Client components
useEasyPdf is a React hook and must be called in a client component. Mark the file with "use client":
// app/components/ReportDownloader.tsx
"use client";
import { useEasyPdf } from "@easypdf/react";
export function ReportDownloader() {
const { pdfRef, downloadPDF, isLoading } = useEasyPdf({
pageSize: "A4",
margins: { top: 20, right: 20, bottom: 20, left: 20 },
footer: { text: "Page {pageNumber} of {totalPages}", align: "center", fontSize: 10 },
});
return (
<div>
<button onClick={() => downloadPDF(pdfRef, { filename: "report.pdf" })} disabled={isLoading}>
{isLoading ? "Generating..." : "Download Report"}
</button>
<div ref={pdfRef}>
{/* Report content */}
</div>
</div>
);
}// app/page.tsx — server component
import { ReportDownloader } from "./components/ReportDownloader";
export default function Page() {
return <ReportDownloader />;
}Using createPDF / createPDFBlob in Next.js
Programmatic PDF generation works the same way — it just needs to be inside a "use client" component and called from a user event:
"use client";
import { useEasyPdf } from "@easypdf/react";
export function InvoiceExporter({ invoice }: { invoice: { id: string; total: number } }) {
const { createPDF, createPDFBlob, isLoading } = useEasyPdf();
const handleDownload = () =>
createPDF(
<div style={{ padding: "32px", fontFamily: "Arial, sans-serif" }}>
<h1>Invoice #{invoice.id}</h1>
<p>Total: ${invoice.total}</p>
</div>,
{ filename: `invoice-${invoice.id}.pdf` }
);
const handleUpload = async () => {
const blob = await createPDFBlob(
<div style={{ padding: "32px", fontFamily: "Arial, sans-serif" }}>
<h1>Invoice #{invoice.id}</h1>
<p>Total: ${invoice.total}</p>
</div>
);
// Send blob to your API route
const formData = new FormData();
formData.append("file", blob, `invoice-${invoice.id}.pdf`);
await fetch("/api/invoices/upload", { method: "POST", body: formData });
};
return (
<div style={{ display: "flex", gap: "8px" }}>
<button onClick={handleDownload} disabled={isLoading}>
{isLoading ? "Generating..." : "Download Invoice"}
</button>
<button onClick={handleUpload} disabled={isLoading}>
Upload to Server
</button>
</div>
);
}Why PDF generation must run in the browser
PDF generation depends on browser APIs that do not exist on the server:
html2canvas— captures a DOM element by reading its layout and computed styles via browser APIsjsPDF— builds the PDF binary, requiresBlob,ArrayBuffer, and other browser globalsReactDOM.createRoot— used in programmatic mode to render JSX off-screen
Calling downloadPDF, createPDF, or createPDFBlob on the server will throw with a clear error message. Always call these methods from user events (button clicks, form submissions) inside "use client" components.
React version support
@easypdf/react supports React 18 and React 19. No extra configuration is needed for either version.