
はじめに
PDF は、外観が全体的に一貫しており、さまざまなリーダーで表示できる優れたドキュメント形式です。
同様に、JavaScript と React は Web 開発において広く使われています。かつては React アプリのスキャフォールディングに create-react-app が定番ツールでしたが、現在は非推奨となり、多くのユーザーが代わりに Vite を使用しています。
この記事では、Vite を使用して、Apryse WebViewer を使用するアプリをスキャフォールディングし、DOCX や PDF を含むさまざまなファイル タイプをブラウザー内で完全に表示および編集できるようにします。
さらに、WebViewer は使い始めるのが非常に簡単なので、約 10 分で動作を開始できるはずです。
Vite は create-react-app と若干動作が異なり、デフォルトのスキャフォールディングでは行き止まりに陥ることがあります。それらについても確認し、簡単に解決できる方法を見ていきます。
設定について
この記事では Windows 11 を使用していますが、表示されるものはすべて macOS および Linux でも動作するはずです。
npm と Node がインストールされている必要があります。この記事では Node v22 を使用しています。
ステップ 1: create-vite を使用してアプリをスキャフォールディングする
ターミナル ウィンドウで、次のコマンドを使用して新しいアプリを作成します。
npx create-vite@latest
これにより、一連の手順が実行され、プロジェクト名、フレームワーク (React を選択)、バリアント (JavaScript を選択) を指定するように求められます。

しばらくすると、プロジェクトの足場が組まれ、開始方法がアドバイスされます。

ディレクトリに移動し、npm install を使用して依存関係をインストールします。
必要であれば、この時点でプロジェクトを実行できます。現時点ではデフォルトの「Vite + React」アプリのみですが、実行することですべてが正常に動作していることを確認できます。

ステップ 2: デフォルト コードを削除する
Vite が完全に機能するアプリのスキャフォールディングを行うのは素晴らしいことですが、作成されたものの多くは必要ないので、削除しましょう。
コマンドラインまたは別の IDE からこれを行うこともできますが、ここでは VSCode でプロジェクトを開きます。
まず、App.css ファイルの内容をすべて削除します。プロトタイプから本番環境に移行する際には、必要に応じて内容を追加していくことになりますが、今はすべての内容を削除しても構いません。

この時点で index.css ファイルの内容を削除することもできますが、Vite スキャフォールディング アプリで WebViewer を使用しようとした場合に発生する可能性のある問題を解決する方法を示しているため、今はそのままにしておきます。
次に、 App.jsx ファイルからほぼすべてを削除します。

ステップ 3: WebViewer をインストールする
これで再構築を始められる段階になりました。Apryse WebViewer を追加する必要がありますが、これは npm install を使って実行できます。Apryse は以前は PDFTron として知られていましたが、2023年にブランド名が変更されました。そのため、WebViewer パッケージのタイトルには「pdftron」という名前が残っています。
npm install @pdftron/webviewer
これにより、パッケージが node_modules にコピーされ、package.json ファイルに追加されます。

ステップ 4: Core フォルダと UI フォルダをコピーする
WebViewer は JavaScript で記述されており、ブラウザー内での使用を想定しています。そのため、実行時にコードがファイルにアクセスできることを確認する必要があります。セキュリティ上の理由から、React はプロジェクトの public フォルダ内のファイルにのみアクセスでき、 node_modules フォルダ内のファイルにはアクセスできません。
Apryse GitHub サンプル リポジトリを検索すると、 WebViewer ファイルをポストインストール スクリプトとして自動的にコピーするコードが見つかります。ただし、ここでは node_modules にある core フォルダと ui フォルダを public フォルダに手動でコピーするだけにします。
ファイルはそのフォルダー内のどこにでも置くことができますが、プロジェクトを適切に構造化するために、新しいフォルダー lib\webviewer に置くことをお勧めします。

ステップ 5: WebViewer をマウントする HTML 要素を作成する
App.jsx ファイル内に、WebViewer をマウントする div 要素を作成するための HTML を追加します。この要素は useRef で指定します。
App.jsx ファイル全体は次のようになります。
import {useRef} from 'react'
import './App.css'
function App() {
const viewer = useRef(null)
return (
<>
<div className='webviewer' ref={viewer}></div>
</>
)
}
export default App
ステップ 6: WebViewer のインスタンス化
WebViewer は useEffect フック内でインスタンス化されます。必要なオプションを指定し、マウントする要素も含める必要があります。これについては後ほど説明します。
重要な WebViewer オプションは次の 2 つです。
- path: WebViewer の core フォルダーと ui フォルダーをコピーした場所を示します。
- licenseKey: 試用版または商用版ライセンスが含まれます。まだお持ちでない場合は、Apryse では無償トライアルも提供しています。
使用できるオプションは他にも多数あります (WebViewer のドキュメントを参照)。ただし、ここでは initialDoc を使用して、WebViewer の作成時に読み込むファイルを指定します。
WebViewer コンストラクタは非同期で、Promise を返します。このコンストラクタが解決されると、インスタンス オブジェクトを使って WebViewer を操作できるようになります。このシンプルなアプリでは今のところ必要ありませんが、WebViewer の機能をさらに詳しく調べたいときに使えるように、空のコードブロックを作成します。
App.jsx ファイル全体は次のようになります。
import { useRef, useEffect } from 'react'
import WebViewer from '@pdftron/webviewer'
import './App.css'
function App() {
const viewer = useRef(null)
useEffect(() => {
WebViewer({
path: 'lib/webviewer',
licenseKey: [your license key],
initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf',
}, viewer.current).then((instance) => {
})
}, [])
return (
<>
<div className='webviewer' ref={viewer}></div>
</>
)
}
export default App
この時点でコードは事実上完了しているので、 npm run dev を使用してプロジェクトを開始できます。
ただし、現時点では WebViewer は表示されません。ブラウザの DevTools ウィンドウを見ると、WebViewer に関するメッセージが表示されています。

これは WebViewer の問題ではなく、CSS の問題です。
ステップ 7: CSS の問題の解決
実際には、これは人為的な問題です。実際のアプリを作成するときは、そのアプリの CSS を慎重に作成するために多くの時間を費やすことになります。
しかし、Vite が作成するデモ アプリは、ログとボタンをいくつか表示するための見栄えの良いデザインになっていますが、これでは必要な機能を果たしていません。実際、body タグ用の CSS を除いて、 index.css からすべてを削除できます (ただし、body タグは必要に応じて別の方法でスタイル設定できます)。同時に、webviewer クラスのスタイルも追加します (そのスタイル設定は App.css で行うべきですが、これはあくまで概念実証です)。
したがって、 index.css の内容全体を次のように置き換えます。
body {
place-items: center;
}
.webviewer{
height: 80vh;
width: 80vw;
}
ページをリロードすると、完全に機能する WebViewer が表示されます。素晴らしいですね!

ステップ 8: 厳格モードで問題を解決する
まだ一つ問題が残っています。
図 9 はブラウザーで実行されている WebViewer を示していますが、2 番目の WebViewer インスタンスが存在します。
繰り返しますが、これは WebViewer の問題ではなく、React の最新バージョンに起因しています。React v18 より前のバージョンでは、空の依存関係配列を持つ useEffect はページの読み込み時に 1 回だけ実行され、WebViewer の構築に使用されていました。
しかし、React v18 以降、StrictMode が指定されている場合 (Vite はデフォルトで StrictMode を指定しています)、開発ビルドでは useEffect が 2 回実行されます。これは本番ビルドでは問題にならないため、人為的な問題です。
開発ビルドでこの問題を解決する簡単な方法は、StrictMode タグを削除することです。

StrictMode を削除するのは良い解決策とは言えません。結局のところ、メモリ リークを検出するために実装されたものです。概念実証では問題にならないかもしれませんが、より複雑なプロジェクトでは間違いなく問題になるでしょう。
より良い解決策 (本番環境の準備ができたら削除できます) は、WebViewer がインスタンス化されたかどうかを記録する別の useRef 定数を追加して、2 度目の発生を回避することです。
import { useRef, useEffect } from 'react'
import WebViewer from '@pdftron/webviewer'
import './App.css'
function App() {
const viewer = useRef(null)
const instantiated = useRef(false)
useEffect(() => {
if (!instantiated.current)
WebViewer({
path: 'lib/webviewer',
licenseKey: [Your license key],
initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/demo-annotated.pdf',
}, viewer.current).then((instance) => {
})
instantiated.current = true
}, [])
return (
<>
<div className='webviewer' ref={viewer}></div>
</>
)
}
export default App
これでプロジェクトを再度実行でき、開発ビルドでも WebViewer が 1 つだけになります。

次のステップ
WebViewer をブラウザー内で実行できるようになるのは、ほんの始まりに過ぎません。豊富な機能が組み込まれているため、PDF ファイルや DOCX ファイルなどの様々なファイル形式をブラウザー内で直接編集できるようになります。Office をインストールする必要もありません。
Apryse では無償トライアルを提供しています。是非お試しください。
Apryse 製品の詳細は、弊社 Web サイトをご確認ください。
記事参照:
© 2025 Apryse
「Creating an App for Viewing and Editing PDFs Using JavaScript, React, and Vite in 10 Minutes」