Blog Post

How to Build a React PDF Viewer with PDF.js

Illustration: How to Build a React PDF Viewer with PDF.js

In this blog, we’ll implement a React PDF.js viewer using PDF.js in Next.js, a popular React framework. React.js is a JavaScript library for building user interfaces. It’s maintained by Facebook and a community of individual developers and companies. React can be used as a base in developing single-page or mobile applications.

Mozilla’s PDF.js is a JavaScript library that allows users to render PDF documents on the web. It focuses on parsing and rendering PDF files.

In the first section, we’ll create the PDF viewer with PDF.js. In the second section, we’ll create a PDF viewer using our JavaScript PDF library. Our viewer library provides some benefits beyond what PDF.js delivers, including:

  • A prebuilt UI — Save time with a well-documented list of APIs when customizing the UI to meet your exact requirements.
  • Annotation tools — Draw, circle, highlight, comment, and add notes to documents with 15+ prebuilt annotation tools.
  • Multiple file types — Support client-side viewing of PDFs, MS Office documents, and image files.
  • 30+ features — Easily add features like PDF editing, digital signatures, form filling, real-time document collaboration, and more.
  • Dedicated support — Deploy faster by working 1-on-1 with our developers.

Integrating a PDF.js Viewer with React

PDF.js consists of three different layers:

  • Core — This layer handles the parsing of the binary PDF file. This layer isn’t usually relevant to the user, as the library uses this internally.

  • Display — This layer uses the core layer and exposes a more straightforward API to render PDFs and expose details.

  • Viewer — The viewer is built on top of the display layer and is the UI for the PDF viewer in Firefox and the other browser extensions within the project.

We’ll use the APIs provided by the display layer to render the PDF, but before we do that, we need to scaffold the React application:

npx create-next-app@latest pdfjs-demo

The above code will scaffold the project inside the pdfjs-demo folder. Then, we’ll have to install the distribution build of PDF.js. It’s available on npm under the name pdfjs-dist:

cd pdfjs-demo && npm install pdfjs-dist

PDF.js uses workers to parse and render the PDF file. To keep things simple, we’ll avoid bundling the workers and instead copy them to our public folder (named public) using the following:

cp ./node_modules/pdfjs-dist/build/pdf.worker.min.js ./public/

If we want to avoid copying the script manually, we can add it to our dev and build scripts in the package.json file:

"scripts": {
+  "copy-assets": "cp ./node_modules/pdfjs-dist/build/pdf.worker.min.js ./public/",
-  "dev": "next dev",
-  "build": "next build",
+  "dev": "npm run copy-assets && next dev",
+  "build": "npm run copy-assets && next build",
}

Now we’re ready to write the code for our application. We’ll start by writing the pages/index.js file. This file is the entry point for our application.

PDF.js renders the PDF in the canvas, so we’ll have to create a canvas and pass its 2D context to the PDF.js library. Let’s render the first page of the PDF in the canvas:

// pages/index.js
import React, { useEffect, useRef } from 'react';

export default function App() {
	const canvasRef = useRef(null);

	useEffect(() => {
		(async function () {
			// We import this here so that it's only loaded during client-side rendering.
			const pdfJS = await import('pdfjs-dist/build/pdf');
			pdfJS.GlobalWorkerOptions.workerSrc =
				window.location.origin + '/pdf.worker.min.js';
			const pdf = await pdfJS.getDocument('example.pdf').promise;

			const page = await pdf.getPage(1);
			const viewport = page.getViewport({ scale: 1.5 });

			// Prepare canvas using PDF page dimensions.
			const canvas = canvasRef.current;
			const canvasContext = canvas.getContext('2d');
			canvas.height = viewport.height;
			canvas.width = viewport.width;

			// Render PDF page into canvas context.
			const renderContext = { canvasContext, viewport };
			page.render(renderContext);
		})();
	}, []);

	return <canvas ref={canvasRef} style={{ height: '100vh' }} />;
}

The example.pdf file in the above code is present in the public directory. The useEffect hook with no dependencies gets executed once, just after the component is first rendered.

We can now run the application using npm run dev, and the application will open on the localhost:3000 port.

As you can see, we can only display the first page of our document by default. If we need any other functionality — like page navigation, search, or annotations — we’ll have to implement it ourselves.

Building a React PDF Viewer with PSPDFKit

PSPDFKit offers a versatile React PDF viewer library that provides more than 30 out-of-the-box features, including:

To start, we’ll install pspdfkit as a dependency of the project:

npx create-next-app@latest pspdfkit-demo
cd pspdfkit-demo
npm install pspdfkit

We have to copy the PSPDFKit for Web library assets to the public directory. We can do this by running the following command:

cp -R ./node_modules/pspdfkit/dist/pspdfkit-lib public/pspdfkit-lib

The above code will copy the pspdfkit-lib directory from within node_modules/ into the public/ directory to make it available to the SDK at runtime. We can add this to package.json, just like in the previous section. Also, make sure document.pdf is present in the public directory:

// app/page.tsx
"use client";
import { useEffect, useRef } from "react";

const App: React.FC = () => {
  const containerRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const container = containerRef.current;

    if (container && typeof window !== "undefined") {
      import("pspdfkit").then((PSPDFKit) => {
        if (PSPDFKit) {
          PSPDFKit.unload(container);
        }

        PSPDFKit.load({
          container,
          document: "/document.pdf",
          baseUrl: `${window.location.protocol}//${window.location.host}/`,
        });
      });
    }
  }, []);

  return <div ref={containerRef} style={{ height: "100vh" }} />;
};

export default App;

Now, when we run npm run dev, the application will open on the localhost:3000 port. We can see that all the features we expect from a PDF viewer are present by default.

Conclusion

This tutorial looked at how to build a React PDF viewer with both the PDF.js open source library and our React PDF library that enables you to display and render PDF files in your React application.

We created similar how-to blog posts using different web frameworks and libraries:

Open source React libraries are good options for building the UI and features yourself. However, depending on the complexity of your use case, development time and costs can quickly increase. In these situations, opting for a commercial solution can speed up development time and lets you focus on other areas of your business.

At PSPDFKit, we offer a commercial, feature-rich, and completely customizable PDF SDK that’s easy to integrate and comes with well-documented APIs to handle advanced use cases. Try it for free, or visit our demo to see it in action.

Related Products
Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
PRODUCTS  |  Web • Releases • Components

PSPDFKit for Web 2024.3 Features New Stamps and Signing UI, Export to Office Formats, and More

PRODUCTS  |  Web • Releases • Components

PSPDFKit for Web 2024.2 Features New Unified UI Icons, Shadow DOM, and Tab Ordering

PRODUCTS  |  Web

Now Available for Public Preview: New Document Authoring Experience Provides Glimpse into the Future of Editing