React PDF Reference

Generate downloadable PDF documents from React component definitions with @react-pdf/renderer.

Imports and concepts

import { Download } from "lucide-react";

const pdfStyles = StyleSheet.create({
  page: { padding: 40 },
  title: { fontSize: 24, marginBottom: 20, fontWeight: "bold" },
  body: { fontSize: 12, lineHeight: 1.5 },
});

function ReportPdf({ title }: { title: string }) {
  return (
    <Document title={title}>
      <Page size="A4" style={pdfStyles.page}>
        <Text style={pdfStyles.title}>{title}</Text>
        <Text style={pdfStyles.body}>This PDF was generated from app data.</Text>
      </Page>
    </Document>
  );
}

export default function App() {
  const { download } = useDownload();
  const [generating, setGenerating] = React.useState(false);

  const handleDownload = async () => {
    setGenerating(true);
    try {
      const blob = await pdf(<ReportPdf title="Monthly Report" />).toBlob();
      download(blob, "report.pdf");
    } finally {
      setGenerating(false);
    }
  };

  return (
    <button onClick={handleDownload} disabled={generating}>
      {generating ? <span className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" /> : <Download className="h-4 w-4" />}
      Download PDF
    </button>
  );
}

Components and layout

Document is the root wrapper and can hold metadata like title, author, subject, and creator. Page defines size, orientation, wrapping, and page styles. View is a flexbox container, Text renders all visible text, Image embeds URLs or data URIs, and Link creates PDF hyperlinks.

<Document title="Invoice" author="Jane Doe">
  <Page size="A4" orientation="portrait" style={pdfStyles.page}>
    <View style={ { flexDirection: "row", gap: 10 } }>
      <Text style={ { flex: 1 } }>Left column</Text>
      <Text style={ { flex: 1 } }>Right column</Text>
    </View>
    <Image src={logoUrl} style={ { width: 100, height: 50 } } />
  </Page>
</Document>

All visible strings must be inside Text. Raw text outside Text will error.

Styles and fonts

Use StyleSheet.create() instead of Tailwind classes. React PDF supports a subset of CSS with flexbox layout and point-based measurements.

const pdfStyles = StyleSheet.create({
  page: { padding: 40, backgroundColor: "#ffffff" },
  header: {
    fontSize: 20,
    fontWeight: "bold",
    marginBottom: 20,
    borderBottomWidth: 1,
    borderBottomColor: "#e5e7eb",
    paddingBottom: 10,
  },
  row: { flexDirection: "row", paddingVertical: 8 },
  cell: { flex: 1, fontSize: 10 },
});

Register custom fonts from app asset URLs when needed.

Font.register({
  family: "Roboto",
  fonts: [
    { src: "https://...blob-url.../Roboto-Regular.ttf" },
    { src: "https://...blob-url.../Roboto-Bold.ttf", fontWeight: "bold" },
  ],
});

Multi-page documents

Content wraps to additional pages by default. Use wrap={false} for rows that should stay together, break to force a new page before a section, and fixed for repeated headers or footers.

<Page size="A4" style={ { padding: 40 } }>
  <Text fixed style={ { fontSize: 9, color: "#9ca3af", marginBottom: 20 } }>
    Company Report - Confidential
  </Text>

  {items.map((item) => (
    <View key={item.id} wrap={false} style={ { marginBottom: 12 } }>
      <Text style={ { fontWeight: "bold" } }>{item.title}</Text>
      <Text style={ { fontSize: 10 } }>{item.description}</Text>
    </View>
  ))}
</Page>

Best practices

Use pdf().toBlob() with useDownload, keep PDF components separate from DOM UI, put every visible string in Text, name the style object pdfStyles, show a loading state during generation, use fixed for repeated headers and footers, use wrap={false} for indivisible rows, and use break for intentional page boundaries.