React PDFの実装方法|PDF生成・表示を簡単に実現

ReactアプリケーションでPDFの生成と表示を実装する方法を詳しく解説。react-pdf、jsPDF、html2canvasを使った実践的な実装例を紹介します。

Learning Next 運営
42 分で読めます

みなさん、ReactでPDFを作りたいと思ったことはありませんか?

「レポートをPDFで出力したい」「請求書を生成したい」と悩んだ経験はありませんか?

この記事では、ReactでPDFの生成と表示を簡単に実装する方法を解説します。 難しそうに見えますが、実は適切なライブラリを選べば意外と簡単なんです。

一緒にPDF機能をマスターして、プロフェッショナルなアプリを作りましょう!

React PDFライブラリの種類

PDF生成ライブラリとPDF表示ライブラリ

ReactでPDFを扱うライブラリには、大きく2つの種類があります。

まずPDF生成ライブラリから見てみましょう。

// PDF生成の主要ライブラリ
const pdfGenerationLibraries = {
  'react-pdf': 'React風の記法でPDF生成',
  'jsPDF': '軽量でシンプルなPDF生成',
  'html2canvas + jsPDF': '画面キャプチャからPDF作成'
};

PDF生成ライブラリは、データからPDFファイルを作り出すためのものです。 請求書やレポートなど、新しくPDFを作成したい場合に使います。

次にPDF表示ライブラリを見てみましょう。

// PDF表示の主要ライブラリ
const pdfViewerLibraries = {
  'react-pdf': 'PDF表示・閲覧機能',
  'pdf-viewer-reactjs': 'シンプルなPDF表示'
};

PDF表示ライブラリは、既存のPDFファイルを画面に表示するためのものです。 PDFビューアーやドキュメント閲覧機能を作りたい場合に使います。

用途に応じたライブラリ選択

プロジェクトの要件に応じて、最適なライブラリを選びましょう。

高品質なPDF生成が必要な場合@react-pdf/rendererがおすすめです。 React風の記法で複雑なレイアウトが作れます。 請求書や証明書など、見た目が重要な書類に最適です。

シンプルなPDF生成で十分な場合jsPDFが良いでしょう。 軽量で学習コストが低く、データ出力やリスト印刷に向いています。

既存画面をPDF化したい場合html2canvas + jsPDFを使います。 既存のReactコンポーネントをそのまま活用できるので、実装が簡単です。

PDF閲覧機能が必要な場合react-pdf(表示用)を選択します。 ページング、ズーム、検索など豊富な閲覧機能が使えます。

@react-pdf/renderer でPDF生成

基本的なPDF作成

@react-pdf/rendererは、React風の記法でPDFを生成できるライブラリです。

まずはインストールから始めましょう。

npm install @react-pdf/renderer

@react-pdf/rendererをインストールすると、React風の記法でPDFを作成できるようになります。

シンプルなPDFドキュメントを作ってみましょう。

import React from 'react';
import { 
  Document, Page, Text, View, StyleSheet, PDFDownloadLink 
} from '@react-pdf/renderer';

// スタイルの定義
const styles = StyleSheet.create({
  page: {
    flexDirection: 'column',
    backgroundColor: '#FFFFFF',
    padding: 30,
  },
  section: {
    margin: 10,
    padding: 10,
    flexGrow: 1
  },
  title: {
    fontSize: 24,
    marginBottom: 20,
    textAlign: 'center',
    color: '#2C3E50'
  },
  text: {
    fontSize: 12,
    marginBottom: 10,
    lineHeight: 1.5
  }
});

// PDFドキュメントコンポーネント
const MyDocument = ({ data }) => (
  <Document>
    <Page size="A4" style={styles.page}>
      <View style={styles.section}>
        <Text style={styles.title}>サンプルドキュメント</Text>
        <Text style={styles.text}>
          これはReact PDFで生成されたドキュメントです。
        </Text>
        <Text style={styles.text}>
          作成日: {new Date().toLocaleDateString('ja-JP')}
        </Text>
      </View>
    </Page>
  </Document>
);

この例では、DocumentPageTextViewなどのコンポーネントを使っています。 ReactのJSXと同じような感覚で、PDFの構造を記述できます。

StyleSheet.createでスタイルを定義し、各コンポーネントに適用します。 CSSのような記法でレイアウトを制御できるので、Web開発者には馴染みやすいですね。

請求書PDFの作成例

実際のビジネスで使える請求書PDFを作ってみましょう。

全体のコード構造を見てから、部分的に解説します。

import React from 'react';
import {
  Document, Page, Text, View, StyleSheet
} from '@react-pdf/renderer';

// 請求書スタイル
const invoiceStyles = StyleSheet.create({
  page: {
    fontSize: 11,
    paddingTop: 20,
    paddingLeft: 40,
    paddingRight: 40,
    lineHeight: 1.5,
    flexDirection: 'column',
  },
  header: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 20
  },
  companyInfo: {
    flex: 1
  },
  invoiceInfo: {
    textAlign: 'right'
  },
  table: {
    display: 'table',
    width: 'auto',
    borderStyle: 'solid',
    borderWidth: 1,
    borderRightWidth: 0,
    borderBottomWidth: 0
  },
  tableRow: {
    flexDirection: 'row'
  },
  tableColHeader: {
    width: '25%',
    borderStyle: 'solid',
    borderWidth: 1,
    borderLeftWidth: 0,
    borderTopWidth: 0,
    backgroundColor: '#DEDEDE',
    padding: 8
  },
  tableCol: {
    width: '25%',
    borderStyle: 'solid',
    borderWidth: 1,
    borderLeftWidth: 0,
    borderTopWidth: 0,
    padding: 8
  }
});

// 請求書ドキュメント
const InvoicePDF = ({ invoiceData }) => (
  <Document>
    <Page size="A4" style={invoiceStyles.page}>
      <View style={invoiceStyles.header}>
        <View style={invoiceStyles.companyInfo}>
          <Text>{invoiceData.company.name}</Text>
          <Text>{invoiceData.company.address}</Text>
        </View>
        <View style={invoiceStyles.invoiceInfo}>
          <Text>請求書</Text>
          <Text>No: {invoiceData.invoiceNumber}</Text>
        </View>
      </View>
      
      <View style={invoiceStyles.table}>
        <View style={invoiceStyles.tableRow}>
          <View style={invoiceStyles.tableColHeader}>
            <Text>項目</Text>
          </View>
          <View style={invoiceStyles.tableColHeader}>
            <Text>数量</Text>
          </View>
          <View style={invoiceStyles.tableColHeader}>
            <Text>単価</Text>
          </View>
          <View style={invoiceStyles.tableColHeader}>
            <Text>金額</Text>
          </View>
        </View>
        
        {invoiceData.items.map((item, index) => (
          <View key={index} style={invoiceStyles.tableRow}>
            <View style={invoiceStyles.tableCol}>
              <Text>{item.description}</Text>
            </View>
            <View style={invoiceStyles.tableCol}>
              <Text>{item.quantity}</Text>
            </View>
            <View style={invoiceStyles.tableCol}>
              <Text>¥{item.unitPrice.toLocaleString()}</Text>
            </View>
            <View style={invoiceStyles.tableCol}>
              <Text>¥{(item.quantity * item.unitPrice).toLocaleString()}</Text>
            </View>
          </View>
        ))}
      </View>
    </Page>
  </Document>
);

まずヘッダー部分を見てみましょう。

<View style={invoiceStyles.header}>
  <View style={invoiceStyles.companyInfo}>
    <Text>{invoiceData.company.name}</Text>
    <Text>{invoiceData.company.address}</Text>
  </View>
  <View style={invoiceStyles.invoiceInfo}>
    <Text>請求書</Text>
    <Text>No: {invoiceData.invoiceNumber}</Text>
  </View>
</View>

flexDirection: 'row'で左右に分割し、会社情報と請求書情報を配置しています。 justifyContent: 'space-between'で両端に配置されるようにしています。

次にテーブル部分を見てみましょう。

<View style={invoiceStyles.table}>
  <View style={invoiceStyles.tableRow}>
    <View style={invoiceStyles.tableColHeader}>
      <Text>項目</Text>
    </View>
    {/* 他のヘッダー */}
  </View>
  
  {invoiceData.items.map((item, index) => (
    <View key={index} style={invoiceStyles.tableRow}>
      {/* データ行 */}
    </View>
  ))}
</View>

テーブルはViewコンポーネントの組み合わせで作成します。 map関数を使って動的にデータ行を生成しています。

動的データでレポート生成

API から取得したデータを使って、動的にレポートを生成してみましょう。

import React, { useState, useEffect } from 'react';

const ReportGenerator = () => {
  const [reportData, setReportData] = useState(null);
  const [loading, setLoading] = useState(false);
  
  const generateSampleData = () => {
    return {
      period: '2025年1月 - 2025年3月',
      totalSales: 1250000,
      totalTransactions: 245,
      products: [
        { name: '商品A', quantity: 50, price: 10000 },
        { name: '商品B', quantity: 30, price: 15000 },
        { name: '商品C', quantity: 70, price: 8000 }
      ]
    };
  };
  
  const handleGenerateReport = async () => {
    setLoading(true);
    
    // 実際のアプリではAPIからデータを取得
    setTimeout(() => {
      const data = generateSampleData();
      setReportData(data);
      setLoading(false);
    }, 1000);
  };
  
  return (
    <div>
      <h2>売上レポート生成</h2>
      
      <button onClick={handleGenerateReport} disabled={loading}>
        {loading ? 'データ取得中...' : 'レポートデータを生成'}
      </button>
      
      {reportData && (
        <PDFDownloadLink
          document={<SalesReportPDF reportData={reportData} />}
          fileName={`sales-report-${Date.now()}.pdf`}
        >
          {({ loading }) =>
            loading ? (
              <button disabled>PDF生成中...</button>
            ) : (
              <button>レポートをダウンロード</button>
            )
          }
        </PDFDownloadLink>
      )}
    </div>
  );
};

handleGenerateReportでデータを取得し、state に保存します。 データが準備できたらPDFDownloadLinkでダウンロードボタンを表示します。

実際のアプリケーションでは、fetchaxiosを使ってAPIからデータを取得することになりますね。

jsPDFでシンプルなPDF生成

基本的なPDF作成

jsPDFは、シンプルなAPIでPDFを生成できるライブラリです。

まずはインストールから始めましょう。

npm install jspdf

jsPDFをインストールすると、JavaScriptの関数を使ってPDFを生成できます。

シンプルなPDF生成の例を見てみましょう。

import jsPDF from 'jspdf';

const SimpleJsPDFGenerator = () => {
  const generatePDF = () => {
    // PDFインスタンスの作成
    const doc = new jsPDF();
    
    // タイトルの追加
    doc.setFontSize(20);
    doc.text('サンプルドキュメント', 20, 30);
    
    // 本文の追加
    doc.setFontSize(12);
    doc.text('これはjsPDFで生成されたPDFです。', 20, 50);
    doc.text('作成日: ' + new Date().toLocaleDateString('ja-JP'), 20, 70);
    
    // 線の描画
    doc.line(20, 80, 190, 80);
    
    // 矩形の描画
    doc.rect(20, 90, 170, 30);
    doc.text('この枠内にコンテンツを配置できます。', 25, 110);
    
    // PDFの保存
    doc.save('simple-document.pdf');
  };
  
  return (
    <div>
      <h2>シンプルPDF生成</h2>
      <button onClick={generatePDF}>
        PDFを生成
      </button>
    </div>
  );
};

最初にnew jsPDF()でPDFドキュメントのインスタンスを作成します。

doc.setFontSize()でフォントサイズを設定し、doc.text()でテキストを追加します。 座標(x, y)を指定して、要素の位置を決めます。

doc.line()で線を描画し、doc.rect()で矩形を描画できます。

最後にdoc.save()を呼び出すと、PDFファイルがダウンロードされます。

テーブル付きレポート

jsPDF-AutoTableを使って、テーブル付きのレポートを作成してみましょう。

npm install jspdf-autotable

jsPDF-AutoTableをインストールすると、美しいテーブルを簡単に作成できます。

import jsPDF from 'jspdf';
import 'jspdf-autotable';

const TableReportGenerator = () => {
  const generateTablePDF = () => {
    const doc = new jsPDF();
    
    // ヘッダー
    doc.setFontSize(18);
    doc.text('売上レポート', 14, 22);
    
    doc.setFontSize(11);
    doc.text('生成日: ' + new Date().toLocaleDateString('ja-JP'), 14, 35);
    
    // サンプルデータ
    const tableData = [
      ['商品A', '50', '¥10,000', '¥500,000'],
      ['商品B', '30', '¥15,000', '¥450,000'],
      ['商品C', '70', '¥8,000', '¥560,000'],
      ['商品D', '25', '¥12,000', '¥300,000']
    ];
    
    // テーブルの生成
    doc.autoTable({
      head: [['商品名', '販売数', '単価', '売上']],
      body: tableData,
      startY: 45,
      styles: {
        fontSize: 10,
        cellPadding: 3
      },
      headStyles: {
        fillColor: [66, 139, 202],
        textColor: 255,
        fontStyle: 'bold'
      }
    });
    
    doc.save('sales-report.pdf');
  };
  
  return (
    <div>
      <h2>テーブルレポート生成</h2>
      <button onClick={generateTablePDF}>
        基本テーブル生成
      </button>
    </div>
  );
};

doc.autoTable()を使ってテーブルを生成します。

headプロパティでヘッダー行を、bodyプロパティでデータ行を指定します。

stylesでテーブル全体のスタイルを、headStylesでヘッダー行のスタイルを設定できます。

グラフ付きレポートの作成

Chart.jsと組み合わせて、グラフ付きのレポートを作成してみましょう。

import jsPDF from 'jspdf';
import { Chart } from 'chart.js/auto';

const ChartPDFGenerator = () => {
  const generateChartPDF = async () => {
    // チャートの作成
    const canvas = document.createElement('canvas');
    canvas.width = 400;
    canvas.height = 300;
    
    const ctx = canvas.getContext('2d');
    
    const chartData = {
      labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
      datasets: [{
        label: '売上',
        data: [450000, 380000, 420000, 510000, 480000, 550000],
        borderColor: 'rgb(75, 192, 192)',
        backgroundColor: 'rgba(75, 192, 192, 0.2)',
        tension: 0.1
      }]
    };
    
    const chart = new Chart(ctx, {
      type: 'line',
      data: chartData,
      options: {
        responsive: false,
        animation: false
      }
    });
    
    // チャートの描画完了を待つ
    await new Promise(resolve => {
      chart.update();
      setTimeout(resolve, 500);
    });
    
    // チャートを画像として取得
    const chartImage = canvas.toDataURL('image/png');
    
    // PDFの作成
    const doc = new jsPDF();
    
    doc.setFontSize(18);
    doc.text('月別売上レポート', 14, 20);
    
    // チャートの埋め込み
    doc.addImage(chartImage, 'PNG', 14, 65, 160, 120);
    
    // チャートを削除
    chart.destroy();
    
    doc.save('chart-report.pdf');
  };
  
  return (
    <div>
      <h2>グラフ付きレポート生成</h2>
      <button onClick={generateChartPDF}>
        グラフ付きPDFを生成
      </button>
    </div>
  );
};

まずCanvasを作成し、Chart.jsでグラフを描画します。

canvas.toDataURL()でグラフを画像データに変換します。

doc.addImage()でPDFにグラフ画像を埋め込みます。

最後にchart.destroy()でメモリを解放することも忘れずに。

html2canvas + jsPDFで画面キャプチャ

既存コンポーネントのPDF化

既存のReactコンポーネントを、そのままPDFに変換してみましょう。

npm install html2canvas jspdf

html2canvasとjsPDFをインストールすると、画面キャプチャからPDFを生成できます。

import React, { useRef } from 'react';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';

const ScreenCapturePDF = () => {
  const componentRef = useRef();
  
  const generatePDF = async () => {
    const element = componentRef.current;
    
    try {
      // html2canvasでキャプチャ
      const canvas = await html2canvas(element, {
        scale: 2,
        useCORS: true,
        allowTaint: true,
        backgroundColor: '#ffffff'
      });
      
      const imgData = canvas.toDataURL('image/png');
      const pdf = new jsPDF();
      
      // PDFのサイズに合わせて調整
      const imgWidth = 210; // A4幅
      const pageHeight = 295; // A4高
      const imgHeight = (canvas.height * imgWidth) / canvas.width;
      
      // 最初のページ
      pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight);
      
      pdf.save('component-capture.pdf');
    } catch (error) {
      console.error('PDF生成エラー:', error);
    }
  };
  
  return (
    <div>
      <div ref={componentRef} style={{ padding: '20px', backgroundColor: 'white' }}>
        <h1 style={{ color: '#2C3E50', textAlign: 'center' }}>
          ダッシュボードレポート
        </h1>
        
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
          <div style={{ 
            padding: '20px', 
            border: '1px solid #ddd', 
            borderRadius: '8px',
            backgroundColor: '#f8f9fa'
          }}>
            <h3>総売上</h3>
            <p style={{ fontSize: '24px', color: '#28a745' }}>
              ¥2,790,000
            </p>
            <p style={{ color: '#666', fontSize: '14px' }}>前月比 +15%</p>
          </div>
          
          <div style={{ 
            padding: '20px', 
            border: '1px solid #ddd', 
            borderRadius: '8px',
            backgroundColor: '#f8f9fa'
          }}>
            <h3>取引件数</h3>
            <p style={{ fontSize: '24px', color: '#007bff' }}>
              553件
            </p>
            <p style={{ color: '#666', fontSize: '14px' }}>前月比 +8%</p>
          </div>
        </div>
      </div>
      
      <div style={{ marginTop: '20px', textAlign: 'center' }}>
        <button onClick={generatePDF}>
          PDFとして保存
        </button>
      </div>
    </div>
  );
};

useRefを使ってPDF化したい要素を参照します。

html2canvas()で要素をCanvasに変換し、toDataURL()で画像データに変換します。

pdf.addImage()で画像をPDFに追加します。

既存のコンポーネントをそのまま活用できるのが、この方法の最大のメリットです。

高品質キャプチャの設定

より高品質なPDFを生成するための設定を見てみましょう。

const AdvancedCapturePDF = () => {
  const [isGenerating, setIsGenerating] = useState(false);
  
  const generateHighQualityPDF = async () => {
    setIsGenerating(true);
    
    try {
      const element = componentRef.current;
      
      // 高品質設定でキャプチャ
      const canvas = await html2canvas(element, {
        scale: 3, // 高解像度
        useCORS: true,
        allowTaint: true,
        backgroundColor: '#ffffff',
        removeContainer: true,
        logging: false,
        width: element.scrollWidth,
        height: element.scrollHeight
      });
      
      const imgData = canvas.toDataURL('image/jpeg', 0.95);
      
      const pdf = new jsPDF({
        orientation: 'portrait',
        unit: 'mm',
        format: 'a4'
      });
      
      const pdfWidth = pdf.internal.pageSize.getWidth();
      const pdfHeight = pdf.internal.pageSize.getHeight();
      
      const imgWidth = pdfWidth;
      const imgHeight = (canvas.height * pdfWidth) / canvas.width;
      
      pdf.addImage(imgData, 'JPEG', 0, 15, imgWidth, imgHeight);
      
      pdf.save('high-quality-report.pdf');
    } catch (error) {
      console.error('PDF生成エラー:', error);
      alert('PDF生成に失敗しました');
    } finally {
      setIsGenerating(false);
    }
  };
  
  return (
    <div>
      <button 
        onClick={generateHighQualityPDF}
        disabled={isGenerating}
      >
        {isGenerating ? '生成中...' : '高品質PDF生成'}
      </button>
    </div>
  );
};

scale: 3で高解像度でキャプチャします。

image/jpeg形式で圧縮率を指定して、ファイルサイズを調整できます。

isGeneratingでボタンの無効化を行い、ユーザーに生成中であることを知らせます。

react-pdfでPDF表示

基本的なPDFビューアー

PDFファイルを表示するための基本的なビューアーを作ってみましょう。

npm install react-pdf pdfjs-dist

react-pdfをインストールすると、PDFファイルを表示できるようになります。

import React, { useState } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';

// ワーカーの設定
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

const SimplePDFViewer = () => {
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);
  const [pdfFile, setPdfFile] = useState(null);
  
  const onDocumentLoadSuccess = ({ numPages }) => {
    setNumPages(numPages);
    setPageNumber(1);
  };
  
  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file && file.type === 'application/pdf') {
      setPdfFile(file);
    } else {
      alert('PDFファイルを選択してください');
    }
  };
  
  const goToPrevPage = () => {
    if (pageNumber > 1) {
      setPageNumber(pageNumber - 1);
    }
  };
  
  const goToNextPage = () => {
    if (pageNumber < numPages) {
      setPageNumber(pageNumber + 1);
    }
  };
  
  return (
    <div style={{ textAlign: 'center', padding: '20px' }}>
      <h2>PDFビューアー</h2>
      
      <div style={{ marginBottom: '20px' }}>
        <input
          type="file"
          accept=".pdf"
          onChange={handleFileChange}
        />
      </div>
      
      {pdfFile && (
        <>
          <div style={{ marginBottom: '20px' }}>
            <button onClick={goToPrevPage} disabled={pageNumber <= 1}>
              前のページ
            </button>
            
            <span style={{ margin: '0 10px' }}>
              {pageNumber} / {numPages}
            </span>
            
            <button onClick={goToNextPage} disabled={pageNumber >= numPages}>
              次のページ
            </button>
          </div>
          
          <Document
            file={pdfFile}
            onLoadSuccess={onDocumentLoadSuccess}
            loading={<div>PDFを読み込み中...</div>}
            error={<div>PDFの読み込みに失敗しました</div>}
          >
            <Page 
              pageNumber={pageNumber}
              scale={1.0}
              loading={<div>ページを読み込み中...</div>}
            />
          </Document>
        </>
      )}
    </div>
  );
};

まずpdfjs.GlobalWorkerOptions.workerSrcでワーカーファイルを設定します。

DocumentコンポーネントでPDFファイルを読み込みます。 onLoadSuccessでページ数を取得できます。

Pageコンポーネントで指定されたページを表示します。 scaleプロパティで表示倍率を調整できます。

高機能PDFビューアー

ズーム機能や検索機能を持つ高機能ビューアーを作ってみましょう。

const AdvancedPDFViewer = () => {
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);
  const [scale, setScale] = useState(1.0);
  const [pdfFile, setPdfFile] = useState(null);
  const [searchText, setSearchText] = useState('');
  const [rotation, setRotation] = useState(0);
  
  const handleZoomIn = () => {
    setScale(prevScale => Math.min(prevScale + 0.2, 3.0));
  };
  
  const handleZoomOut = () => {
    setScale(prevScale => Math.max(prevScale - 0.2, 0.5));
  };
  
  const handleRotate = () => {
    setRotation(prevRotation => (prevRotation + 90) % 360);
  };
  
  const handleKeyDown = (event) => {
    switch (event.key) {
      case 'ArrowLeft':
        if (pageNumber > 1) setPageNumber(pageNumber - 1);
        break;
      case 'ArrowRight':
        if (pageNumber < numPages) setPageNumber(pageNumber + 1);
        break;
      case '+':
        event.preventDefault();
        handleZoomIn();
        break;
      case '-':
        event.preventDefault();
        handleZoomOut();
        break;
    }
  };
  
  return (
    <div onKeyDown={handleKeyDown} tabIndex={0}>
      <h2>高機能PDFビューアー</h2>
      
      {/* ツールバー */}
      <div style={{ 
        display: 'flex', 
        alignItems: 'center', 
        gap: '10px',
        marginBottom: '20px',
        padding: '10px',
        backgroundColor: '#f8f9fa'
      }}>
        <input type="file" accept=".pdf" onChange={handleFileChange} />
        
        {pdfFile && (
          <>
            <button onClick={() => setPageNumber(Math.max(1, pageNumber - 1))}>
              ⬅️ 前
            </button>
            
            <input
              type="number"
              value={pageNumber}
              onChange={(e) => setPageNumber(parseInt(e.target.value))}
              min="1"
              max={numPages}
              style={{ width: '60px', textAlign: 'center' }}
            />
            
            <span>/ {numPages}</span>
            
            <button onClick={() => setPageNumber(Math.min(numPages, pageNumber + 1))}>
              次 ➡️
            </button>
            
            <button onClick={handleZoomOut}>🔍➖</button>
            <span>{Math.round(scale * 100)}%</span>
            <button onClick={handleZoomIn}>🔍➕</button>
            
            <button onClick={handleRotate}>🔄</button>
            
            <input
              type="text"
              placeholder="検索..."
              value={searchText}
              onChange={(e) => setSearchText(e.target.value)}
            />
          </>
        )}
      </div>
      
      {/* PDF表示エリア */}
      {pdfFile && (
        <div style={{ 
          textAlign: 'center',
          maxHeight: '80vh',
          overflow: 'auto',
          border: '1px solid #ddd'
        }}>
          <Document
            file={pdfFile}
            onLoadSuccess={onDocumentLoadSuccess}
          >
            <Page 
              pageNumber={pageNumber}
              scale={scale}
              rotate={rotation}
              renderTextLayer={true}
              renderAnnotationLayer={true}
            />
          </Document>
        </div>
      )}
    </div>
  );
};

handleZoomInhandleZoomOutでズーム機能を実装します。

handleRotateで90度回転機能を追加します。

handleKeyDownでキーボードショートカットに対応します。

renderTextLayer={true}でテキスト選択を有効にします。

まとめ

ReactでPDF機能を実装する方法について、詳しく解説しました。

ライブラリ選択のポイントをおさらいしましょう:

  • @react-pdf/renderer: React風記法、高品質レイアウト
  • jsPDF: シンプル、軽量、基本的な生成
  • html2canvas + jsPDF: 既存コンポーネント活用
  • react-pdf: 高機能PDFビューアー

用途別の使い分けも覚えておきましょう:

  • 請求書・証明書 → @react-pdf/renderer
  • データ出力・レポート → jsPDF + autoTable
  • ダッシュボード出力 → html2canvas + jsPDF
  • PDF閲覧機能 → react-pdf(表示用)

実装のコツです:

  • パフォーマンス考慮(大量データ処理)
  • エラーハンドリング(適切なフィードバック)
  • レスポンシブ対応(様々な画面サイズ)

PDF機能は、ビジネスアプリケーションで非常に重要な機能です。 適切なライブラリを選択して、ユーザーにとって価値のある機能を提供しましょう。

ぜひ実際のプロジェクトで試してみてください!

関連記事