Export React components as pixel-perfect PDFs
Render your React components to HTML with renderToStaticMarkup, send to DocAPI, get a PDF back. No new JSX DSL to learn, no canvas tricks, no client-side slowness. Preserves all CSS including Tailwind.
Free tier: 100 PDFs/month. No credit card required.
How React-to-PDF works
Three steps — render, send, receive. No client-side JavaScript required in the PDF.
Render to HTML
Use ReactDOMServer.renderToStaticMarkup() to convert your React component to a plain HTML string on the server. Works in Next.js API routes and server actions.
Send to DocAPI
POST the HTML string to https://api.docapi.co/v1/pdf with your API key. Specify page format, margins, and other options in the request body.
Receive PDF bytes
DocAPI renders the HTML in Chromium and returns raw PDF bytes. Stream them directly to the browser as a download or save to cloud storage.
Write a regular React component
Your PDF template is just a React component. Use inline styles or a stylesheet injected via a <style> tag. No special PDF-specific JSX syntax, no learning a new primitive API like react-pdf requires.
- Standard React JSX — no new APIs to learn
- Inline styles or a <style> tag for CSS
- Props-driven — different data per PDF
- Reuse existing components and formatters
- TypeScript support end-to-end
// components/InvoiceTemplate.tsx
export function InvoiceTemplate({ invoice }: { invoice: Invoice }) {
return (
<html>
<head>
<style>{`
body { font-family: system-ui, sans-serif; margin: 0; padding: 40px; }
.header { display: flex; justify-content: space-between; }
.total { font-size: 24px; font-weight: bold; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #e5e7eb; }
`}</style>
</head>
<body>
<div className="header">
<h1>{invoice.companyName}</h1>
<div>
<p>Invoice #{invoice.number}</p>
<p>{invoice.date}</p>
</div>
</div>
<table>
<thead>
<tr><th>Item</th><th>Qty</th><th>Price</th><th>Total</th></tr>
</thead>
<tbody>
{invoice.items.map((item) => (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.qty}</td>
<td>${item.price}</td>
<td>${item.qty * item.price}</td>
</tr>
))}
</tbody>
</table>
<p className="total">Total: ${invoice.total}</p>
</body>
</html>
);
}// app/api/invoice/route.ts
import { renderToStaticMarkup } from "react-dom/server";
import { DocAPI } from "@docapi/sdk";
import { InvoiceTemplate } from "@/components/InvoiceTemplate";
const client = new DocAPI(process.env.DOCAPI_KEY!);
export async function POST(req: Request) {
const data = await req.json();
// Render your React component to an HTML string server-side
const html = renderToStaticMarkup(<InvoiceTemplate {...data} />);
// Send to DocAPI — returns raw PDF bytes
const pdf = await client.pdf(
`<!DOCTYPE html><html><body>${html}</body></html>`,
{ format: "A4", printBackground: true }
);
return new Response(pdf, {
headers: { "Content-Type": "application/pdf" },
});
}Next.js API route — render and convert in one step
In a Next.js API route or server action, call renderToStaticMarkup to get the HTML string, then send it to DocAPI. The whole round-trip — render + convert + stream — is typically under 500ms.
- renderToStaticMarkup — no hydration JS in the PDF
- Works in Next.js App Router route handlers
- Stream PDF bytes directly to the browser
- Set Content-Disposition for inline view or download
- No temp files — fully in-memory
Works with Tailwind — inline the stylesheet
Tailwind generates a CSS file. Read it with fs.readFileSync and inject it into a <style> tag in your HTML wrapper. DocAPI renders the Tailwind-styled HTML in Chromium, so all utility classes work exactly as in the browser.
- All Tailwind utility classes work in PDFs
- Print media queries respected (hidden, print:block)
- Dark mode classes can be excluded via PurgeCSS
- No viewport size restrictions — full A4 width
- Custom fonts via @font-face in the stylesheet
// Works with Tailwind CSS too — inline the stylesheet
import { renderToStaticMarkup } from "react-dom/server";
import { DocAPI } from "@docapi/sdk";
import fs from "fs";
const client = new DocAPI(process.env.DOCAPI_KEY!);
const tailwindCss = fs.readFileSync("public/tailwind.css", "utf-8");
export async function POST(req: Request) {
const data = await req.json();
const componentHtml = renderToStaticMarkup(<ReportTemplate {...data} />);
// Wrap with Tailwind CSS inline
const html = `<!DOCTYPE html>
<html>
<head><style>${tailwindCss}</style></head>
<body class="bg-white p-10">${componentHtml}</body>
</html>`;
const pdf = await client.pdf(html, { format: "A4" });
return new Response(pdf, {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": "inline; filename=report.pdf",
},
});
}// Client component — download PDF from a button click
"use client";
export function DownloadPdfButton({ invoiceData }: { invoiceData: Invoice }) {
const handleDownload = async () => {
const res = await fetch("/api/invoice", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(invoiceData),
});
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `invoice-${invoiceData.number}.pdf`;
a.click();
URL.revokeObjectURL(url);
};
return (
<button onClick={handleDownload}>
Download PDF
</button>
);
}Add a "Download PDF" button to any page
The client component simply fetches the API route and triggers a browser download via a temporary object URL. The PDF generation itself happens entirely on the server — no client-side rendering, no canvas, no jsPDF.
- Click button → fetch API route → trigger download
- PDF generation is 100% server-side
- No client-side PDF library needed
- Works in any React app, not just Next.js
- Can also open inline in browser tab
Common React PDF use cases
Invoice PDFs from React
Build your invoice UI as a React component. Reuse it on-screen and as a PDF — same template, same data, same formatting.
Report exports
Export data-heavy reports as PDFs. Render tables, charts (as SVG), and summaries from your React components with real data.
Certificate generation
Generate styled completion certificates or badges from a React template with dynamic name, date, and course data.
Contract PDFs
Render legal agreements from a React template with per-user data. Server-side generation means consistent, reproducible output.
Proposal and quote PDFs
Sales teams can generate branded proposal PDFs from a React template populated with CRM data, pricing, and custom notes.
Onboarding documents
Welcome packets, onboarding guides, and policy documents generated from React templates with company and employee-specific data.
vs. react-pdf, jsPDF, and html2canvas
Other React PDF tools require learning a new API, run client-side, or produce low-fidelity output.
| Feature | DocAPI | react-pdf | jsPDF | html2canvas |
|---|---|---|---|---|
| Use regular JSX | ✓ | Custom DSL | ✗ | ✓ |
| Server-side generation | ✓ | ✓ | ✗ | ✗ |
| Full CSS fidelity | ✓ | Limited | ✗ | Partial |
| Tailwind support | ✓ | ✗ | ✗ | ✗ |
| No client JS needed | ✓ | ✓ | Client only | Client only |
| Selectable text in PDF | ✓ | ✓ | ✗ | ✗ (image) |
| Free tier | 100/mo | Open source | Open source | Open source |
Start generating PDFs from React today
Free tier includes 100 PDFs per month. No credit card, no new DSL to learn — just render your existing React components to HTML and call the API.