Doc API
React to PDF

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.

1

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.

2

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.

3

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.

Step 1: your React component

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
// 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
// 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" },
  });
}
Step 2 + 3: render and convert

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
Tailwind CSS support

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
app/api/report/route.ts
// 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",
    },
  });
}
components/DownloadPdfButton.tsx
// 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>
  );
}
Client-side trigger

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.

FeatureDocAPIreact-pdfjsPDFhtml2canvas
Use regular JSXCustom DSL
Server-side generation
Full CSS fidelityLimitedPartial
Tailwind support
No client JS neededClient onlyClient only
Selectable text in PDF✗ (image)
Free tier100/moOpen sourceOpen sourceOpen 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.

React to PDF — Export React Components as PDFs | Doc API