@react-pdf/renderer と react-pdf は、どちらも React エコシステムで PDF を扱うためのライブラリですが、その目的と動作原理は完全に異なります。
@react-pdf/renderer は、React コンポーネントの構文を使って PDF ドキュメントを生成するためのライブラリです。独自のレンダラーを持ち、React の要素ツリーを解析してバイナリ形式の PDF ファイルを構築します。サーバーサイド(Node.js)でもクライアントサイド(ブラウザ)でも動作し、動的なレポートや請求書作成に適しています。
一方、react-pdf は、既存の PDF ファイルを Web ブラウザー上で表示するためのライブラリです。Mozilla の pdf.js エンジンをラップしており、PDF ファイルを読み込んで Canvas 要素として描画します。ドキュメントの閲覧、ページネーション、ズーム機能を提供しますが、PDF を新規作成する機能はありません。
React プロジェクトで PDF 処理が必要になった際、@react-pdf/renderer と react-pdf という名前が似ている 2 つのパッケージが存在することに混乱する開発者は少なくありません。しかし、これらは解決する問題が真逆です。一方は「PDF を作る」ためのツールであり、もう一方は「PDF を見る」ためのツールです。この違いを理解せずに選定を誤ると、アーキテクチャ全体が行き詰まることになります。
本記事では、両者の技術的な仕組み、実装方法、そして具体的なユースケースに基づき、どちらを採用すべきかを明確にします。
まず最も重要な点は、両者が扱うデータフローの方向性が反対だということです。
@react-pdf/renderer は、React コンポーネントツリーを入力として受け取り、PDF バイナリデータを出力します。内部に独自のレンダリングエンジンを持っており、HTML や CSS を解釈するのではなく、@react-pdf 固有のコンポーネント(<View>, <Text>, <Page> など)を PDF の構造命令に変換します。
// @react-pdf/renderer: PDF を「生成」するコード
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>請求書番号:12345</Text>
<Text>金額:¥10,000</Text>
</View>
</Page>
</Document>
);
// これを <PDFDownloadLink> や pdf() 関数でバイナリとして出力します
react-pdf は、既存の PDF ファイル(URL や Blob)を入力として受け取り、ブラウザ上の <canvas> 要素に描画します。内部では Mozilla 製の pdf.js を使用しており、PDF の仕様を解析して視覚化します。React コンポーネントとして振る舞いますが、中身はあくまで「ビューワー」です。
// react-pdf: PDF を「表示」するコード
import { Document, Page, pdfjs } from 'react-pdf';
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;
function MyApp() {
return (
<div>
<Document file="/path/to/invoice.pdf">
<Page pageNumber={1} />
</Document>
</div>
);
}
両者は似たようなコンポーネント名(Document, Page)を使っていますが、その実体とプロパティは全く異なります。ここを混同するとビルドエラーや予期せぬ動作の原因になります。
@react-pdf/renderer は、Flexbox に基づいた独自のスタイリングシステムを採用しています。CSS のサブセットのような記述が可能ですが、Web ブラウザの CSS 実装とは異なる制約(例えば、div ではなく View を使う、特定の単位しか使えないなど)があります。これは PDF 仕様という固定された出力形式に合わせているためです。
// @react-pdf/renderer: 独自コンポーネントと Flexbox スタイル
import { StyleSheet } from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: { padding: 30, flexDirection: 'row' },
column: { width: '50%', padding: 10 },
text: { fontSize: 12, fontFamily: 'Helvetica' }
});
const Invoice = ({ data }) => (
<View style={styles.page}>
<View style={styles.column}>
<Text style={styles.text}>{data.sender}</Text>
</View>
<View style={styles.column}>
<Text style={styles.text}>{data.receiver}</Text>
</View>
</View>
);
react-pdf は、PDF ファイルそのものが既にレイアウト情報を持っているため、スタイリングの概念はありません。開発者が制御できるのは、表示されるページのサイズ、スケール、回転、そしてキャンバスの配置だけです。CSS で制御するのは、PDF が描画されるコンテナ要素のみです。
// react-pdf: 表示制御のみ。コンテンツのスタイル変更は不可
function Viewer({ fileUrl }) {
const [numPages, setNumPages] = useState(null);
function onDocumentLoadSuccess({ numPages }) {
setNumPages(numPages);
}
return (
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Document
file={fileUrl}
onLoadSuccess={onDocumentLoadSuccess}
// コンテンツ自体のスタイル変更はここでできない
>
{Array.from(new Array(numPages), (el, index) => (
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
scale={1.5}
renderTextLayer={true}
/>
))}
</Document>
</div>
);
}
@react-pdf/renderer の真価は、React の状態管理やプロパティを活用して、動的にコンテンツを変化させられる点にあります。JavaScript のロジックをそのまま使って、条件分岐やループを行いながら PDF を組み立てられます。
// @react-pdf/renderer: JavaScript ロジックで動的に内容を構築
const OrderItems = ({ items }) => (
<View>
{items.map((item, i) => (
<View key={i} style={{ flexDirection: 'row', borderBottom: '1px solid #eee' }}>
<Text style={{ width: '60%' }}>{item.name}</Text>
<Text style={{ width: '40%', textAlign: 'right' }}>
{item.price.toLocaleString()} 円
</Text>
</View>
))}
</View>
);
react-pdf において、PDF 内部のテキストや画像を React の状態で書き換えることはできません。できるのは「どのページを表示するか」「どのくらい拡大するか」といったビューワーとしての操作のみです。もし内容を書き換えたい場合は、バックエンドなどで別途 PDF を生成し直す必要があります。
// react-pdf: 操作できるのはページ番号や拡大率のみ
function ControlledViewer({ file }) {
const [page, setPage] = useState(1);
return (
<div>
<button onClick={() => setPage(prev => prev - 1)} disabled={page <= 1}>前へ</button>
<span>ページ {page}</span>
<button onClick={() => setPage(prev => prev + 1)}>次へ</button>
<Document file={file}>
<Page pageNumber={page} />
</Document>
</div>
);
}
実行環境のサポート状況も大きな分岐点です。
@react-pdf/renderer は、Node.js 環境での実行を強く意識して設計されています。これにより、Next.js や Remix などのフレームワークを用いたサーバーサイドレンダリング(SSR)や、API ルート内での PDF 生成が容易です。ブラウザ依存の API を極力排除しており、サーバー上で pdf() 関数を呼び出してバイナリストリームを返すような処理が一般的です。
// Node.js (API Route) での使用例
import { pdf } from '@react-pdf/renderer';
import MyDocument from './MyDocument';
export async function GET() {
const blob = await pdf(<MyDocument data={...} />).toBlob();
// blob をレスポンスとして返す
}
react-pdf は、描画に HTML5 Canvas を使用するため、原則としてクライアントサイド(ブラウザ)での実行が必須です。サーバーサイドで PDF を描画することはできません(Node.js で Canvas をエミュレートする試みは存在しますが、公式の主要ユースケースではありません)。SSR を行う場合は、ハイドレーションミスマッチを防ぐために「クライアントmounted 後に表示する」ような制御が必要です。
// react-pdf: クライアントサイド専用としての保護
import { useEffect, useState } from 'react';
function SafeViewer({ file }) {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
if (!isClient) return <div>読み込み中...</div>;
return <Document file={file}><Page pageNumber={1} /></Document>;
}
実務での判断基準を整理します。
@react-pdf/renderer を選ぶべきケースreact-pdf を選ぶべきケースnpm install react-pdf と npm install @react-pdf/renderer を間違えないように注意してください。パッケージ名が似ているため、誤ってインストールしてしまう事故が頻発しています。@react-pdf/renderer では、日本語フォントなどを扱う際に、フォントファイルの登録プロセスが別途必要です。標準のフォントだけでは日本語が表示できない場合があるため、ドキュメントをよく確認してください。react-pdf で巨大な PDF を表示する場合、すべてのページを一度にレンダリングするとブラウザが重くなります。必要に応じて表示範囲を制限する実装が求められます。この 2 つは競合関係ではなく、補完関係にあります。
@react-pdf/renderer です。React の宣言的な記法で柔軟なドキュメントを生成できます。react-pdf が唯一の選択肢です。高機能なビューワーを簡単に実装できます。場合によっては、@react-pdf/renderer で動的に PDF を生成し、生成されたファイルを react-pdf で即時プレビュー表示する、というように両者を組み合わせるアーキテクチャも有効です。それぞれの役割を正しく理解し、プロジェクトの要件に合わせて適切に使い分けてください。
既存の PDF ファイルをアプリ内で表示させたい場合、または PDF の閲覧機能(ズーム、ページ送り)が主な要件である場合は react-pdf を選択してください。これは PDF リーダーやドキュメント管理システムのビューワー部分を実装する際の標準的な選択肢です。ただし、このパッケージで PDF を新規作成することはできません。
React コンポーネントとして PDF ドキュメントをゼロから設計し、動的なデータに基づいて PDF ファイルを生成したい場合は @react-pdf/renderer を選択してください。請求書、レポート、証明書など、コンテンツがユーザーごとに変化するドキュメントをサーバー側またはクライアント側で生成する際に最適です。既存の PDF ファイルを表示する用途には向きません。
React renderer for creating PDF files on the browser and server
yarn add @react-pdf/renderer
import React from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
// Create styles
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
backgroundColor: '#E4E4E4',
},
section: {
margin: 10,
padding: 10,
flexGrow: 1,
},
});
// Create Document Component
const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>Section #1</Text>
</View>
<View style={styles.section}>
<Text>Section #2</Text>
</View>
</Page>
</Document>
);
Web. Render in DOMimport React from 'react';
import ReactDOM from 'react-dom';
import { PDFViewer } from '@react-pdf/renderer';
const App = () => (
<PDFViewer>
<MyDocument />
</PDFViewer>
);
ReactDOM.render(<App />, document.getElementById('root'));
Node. Save in a fileimport React from 'react';
import ReactPDF from '@react-pdf/renderer';
ReactPDF.render(<MyDocument />, `${__dirname}/example.pdf`);
This project exists thanks to all the people who contribute. Looking to contribute? Please check our [contribute] document for more details about how to setup a development environment and submitting code.
Thank you to all our sponsors! [Become a sponsors]
Thank you to all our backers! [Become a backer]
MIT © Diego Muracciole