はじめに
「見積書や請求書をPDFで出力したい」——業務アプリを作っていると必ずと言っていいほどぶつかる要件です。サーバーサイドでHTMLをheadlessブラウザに食わせて印刷する方法もありますが、Chromiumの起動コストやフォントの崩れ、レイアウトの微調整の手間に悩まされた経験がある方も多いのではないでしょうか。
そこで候補に挙がるのが @react-pdf/renderer(GitHub上では diegomura/react-pdf という名前で公開されています)です。ReactのコンポーネントとJSXの構文そのままでPDFの中身を組み立てられるため、Reactに慣れたチームであれば学習コストを抑えつつ、崩れにくい帳票を作れるようになります。
なお、似た名前の react-pdf(wojtekmaj氏によるライブラリ)は既存のPDFを「表示」するためのものですが、本記事で紹介する @react-pdf/renderer はPDFを「生成」するためのライブラリです。用途がまったく異なるので、導入時は混同しないよう注意してください。
@react-pdf/rendererとは
@react-pdf/renderer は、ブラウザとNode.jsの両方で動作するReact用のPDFレンダラーです。Document や Page といった専用コンポーネントを使い、Flexboxベースのレイアウトシステムでスタイリングすることで、通常のReactコンポーネントを書く感覚のままPDFファイルを作成できます。GitHub上で16,000以上のスターを集めており、帳票生成ライブラリとして広く採用されています。
主な特徴
- JSXでPDFを記述できる -
ViewやTextといったおなじみのコンポーネント名でレイアウトを組み立てられ、HTML/CSSの知識がそのまま活きます - Flexboxレイアウト -
flexDirectionやjustifyContentなど、CSSと同じ感覚で複雑な帳票レイアウトを組めます - ブラウザ・サーバー両対応 - クライアントサイドでのプレビュー表示から、サーバーサイドでのファイル生成・バッチ処理まで同じコンポーネントを使い回せます
- 画像・SVG・カスタムフォント対応 - ロゴ画像の埋め込みや自作フォントの登録、SVGによる図形描画もサポートしています
インストール
npm、yarn、pnpmのいずれかでインストールします。
# npm
npm install @react-pdf/renderer
# yarn
yarn add @react-pdf/renderer
# pnpm
pnpm add @react-pdf/renderer
基本的な使い方
まずは最小構成のPDFドキュメントを作成してみましょう。Document の中に Page を配置し、Text や View で内容を組み立てます。
// Invoice.jsx
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';
const styles = StyleSheet.create({
page: {
padding: 30,
fontSize: 12,
},
title: {
fontSize: 20,
marginBottom: 12,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
borderBottom: '1px solid #ccc',
paddingVertical: 4,
},
});
const Invoice = () => (
<Document>
<Page size="A4" style={styles.page}>
<Text style={styles.title}>見積書</Text>
<View style={styles.row}>
<Text>項目</Text>
<Text>金額</Text>
</View>
<View style={styles.row}>
<Text>Webサイト制作</Text>
<Text>¥300,000</Text>
</View>
</Page>
</Document>
);
export default Invoice;
ブラウザ上でプレビューしたい場合は PDFViewer を使うと、iframe感覚でその場に埋め込めます。
import { PDFViewer } from '@react-pdf/renderer';
import Invoice from './Invoice';
const App = () => (
<PDFViewer width="100%" height={600}>
<Invoice />
</PDFViewer>
);
export default App;
ダウンロードボタンを付けたいだけであれば、PDFDownloadLink を使えば生成処理を意識せずに実装できます。
import { PDFDownloadLink } from '@react-pdf/renderer';
import Invoice from './Invoice';
const DownloadButton = () => (
<PDFDownloadLink document={<Invoice />} fileName="invoice.pdf">
{({ loading }) => (loading ? 'PDFを生成中...' : 'PDFをダウンロード')}
</PDFDownloadLink>
);
export default DownloadButton;
実践的なユースケース
サーバーサイドでファイル出力する
Node.jsのバッチ処理やAPIエンドポイントから、直接PDFファイルを書き出すこともできます。月次で大量の請求書を一括生成するような場面で重宝します。
// generate.js
import { renderToFile } from '@react-pdf/renderer';
import Invoice from './Invoice';
async function main() {
await renderToFile(<Invoice />, './output/invoice.pdf');
console.log('PDFの生成が完了しました');
}
main();
ロゴ画像とカスタムフォントを使った帳票
日本語フォントを登録し、会社ロゴを差し込むことで、実際の業務帳票に近い見た目に仕上げられます。
import { Document, Page, Text, View, Image, Font, StyleSheet } from '@react-pdf/renderer';
Font.register({
family: 'NotoSansJP',
src: '/fonts/NotoSansJP-Regular.ttf',
});
const styles = StyleSheet.create({
page: {
padding: 30,
fontFamily: 'NotoSansJP',
},
header: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
logo: {
width: 60,
marginRight: 10,
},
});
const CompanyInvoice = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.header}>
<Image style={styles.logo} src="/images/logo.png" />
<Text>株式会社サンプル</Text>
</View>
<Text>ご請求金額:¥550,000(税込)</Text>
</Page>
</Document>
);
export default CompanyInvoice;
日本語を扱う場合は、フォントに日本語グリフが含まれていないと文字化けするので、Noto Sans JPのようなフォントファイルを別途用意して登録する点に注意してください。
まとめ
@react-pdf/renderer を使えば、ReactのコンポーネントとFlexboxレイアウトの知識をそのまま活かして、見積書や請求書といった帳票をコードベースで管理できます。ブラウザでのプレビューからサーバーサイドでの一括生成まで同じコンポーネントを再利用できる点は、headlessブラウザによるHTML印刷方式にはない大きなメリットです。
まずは簡単な Document と Page の組み合わせから試し、慣れてきたらカスタムフォントやSVGを組み合わせて、自社の帳票デザインに近づけていくとよいでしょう。