How to Build a TypeScript PDF Viewer with PDF.js

![]()
This article was first published in September 2021 and was updated in April 2022.
In this tutorial, we’ll show how to build a TypeScript PDF viewer with PDF.js, one of the most popular open source libraries for rendering PDF files in the browser.
Results for The State of JS 2021 were recently published, and TypeScript is by far the most common alternative flavor of JavaScript. Developers seem to like TypeScript due to its type safety and the fact that it’s a superset of JavaScript. TypeScript adds static typing to JavaScript, which allows developers to catch errors at compile time instead of run time.
In the first part of this tutorial, we’ll walk through how to render a PDF in the browser with PDF.js and TypeScript. In the second part, we’ll look at how to build a fully featured PDF viewer with the PSPDFKit TypeScript PDF library. Our PDF viewer library provides some additional benefits beyond what PDF.js provides, including:
- A prebuilt and polished UI for an improved user experience
- 15+ prebuilt annotation tools to enable document collaboration
- Browser-based text editing, page cropping, merging, rotating, and more
- Support for more file types with client-side PDF, MS Office, and image viewing
- Dedicated support from engineers to speed up integration
Requirements
To get started, you’ll need:
-
A package manager for installing the PSPDFKit library. You can use npm or Yarn. When you install Node.js,
npm
is installed by default. -
TypeScript
You can install TypeScript globally by running the following command:
npm install -g typescript
Building a TypeScript PDF Viewer with PDF.js
PDF.js is a JavaScript library built by Mozilla, and it allows you to create a full-featured PDF viewer in the browser using JavaScript and the HTML5 <canvas>
element. You can integrate PDF.js with different JavaScript frameworks and libraries like React, Angular, and Vue.js.
Getting Started
-
Create a new folder on your computer and change your directory to the project:
mkdir typescript-pdf-viewer cd typescript-pdf-viewer
-
Next, run
npm init --yes
to create apackage.json
file. -
Create a new
tsconfig.json
configuration file at the root of your project:
tsc --init
You can customize the rules you want the TypeScript compiler to follow, such as the following configuration:
// tsconfig.json { "compilerOptions": { "target": "es6", "module": "es6", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "removeComments": true, "preserveConstEnums": true, "sourceMap": true, "noImplicitAny": true, "strictNullChecks": false, "moduleResolution": "node" }, "include": ["src/**/*"] }
With this configuration, the compiled JavaScript code will target the 2015 version of ECMAScript standards for JavaScript, and it’ll use the ES6
import and export modules. include
determines what files the compiler will process. In this case, it’ll check every file in the src
folder.
Check out the compiler options for more information.
Installing PDF.js and Configuring webpack
-
You’ll use one of the most popular build tools, webpack, to bundle your project into a single file. It’ll help you reduce the HTTP requests needed to load the PDF and minify your code.
Start by downloading the necessary dev
dependencies:
npm i -D webpack webpack-cli webpack-dev-server ts-loader typescript html-webpack-plugin cross-env copy-webpack-plugin clean-webpack-plugin
Here’s what’s installed:
-
webpack
— The webpack bundler. -
webpack-cli
— Command-line interface for webpack. -
webpack-dev-server
— A local server to run webpack in the browser. -
ts-loader
— A package that teaches webpack how to compile TypeScript. -
typescript
— The TypeScript compiler. -
clean-webpack-plugin
— A plugin that cleans the output directory before building. -
copy-webpack-plugin
— A plugin that copies files and directories to the output directory. -
html-webpack-plugin
— A plugin that generates an HTML file from a template. -
cross-env
— A package that allows you to set environment variables.
After the installation, you can see the dependencies in your package.json
file:
"devDependencies": { "clean-webpack-plugin": "^4.0.0", "copy-webpack-plugin": "^10.2.4", "cross-env": "^7.0.3", "html-webpack-plugin": "^5.5.0", "ts-loader": "^9.2.7", "typescript": "^4.6.2", "webpack": "^5.70.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.7.4", }
-
To configure webpack, create a
webpack.config.js
file in the root of your project. This will contain parameters that determine how the project will be built:
// webpack.config.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const CleanPlugin = require('clean-webpack-plugin'); module.exports = { entry: path.resolve(__dirname, './src/index.ts'), output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', }, devtool: 'inline-source-map', mode: 'development', module: { rules: [ // All files with a `.ts` or `.tsx` extension will be handled by `ts-loader`. { test: /\.tsx?$/, use: { loader: 'ts-loader' }, exclude: /node_modules/, }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js'], }, plugins: [ new CleanPlugin.CleanWebpackPlugin(), // Automatically insert <script src="[name].js"><script> into the page. new HtmlWebpackPlugin({ template: './src/index.html', }), // Copy the PDF file and PDF.js worker to the output path. new CopyWebpackPlugin({ patterns: [ { from: './src/example.pdf', to: './example.pdf', }, { from: './node_modules/pdfjs-dist/build/pdf.worker.js', to: './main.worker.js', }, ], }), ], };
The entry point of your application will be the src/index.ts
file, and the output will be bundled inside the dist
folder. Note that you’re copying both the PDF file you want to display and the PDF.js worker to the output path.
You created the module
property containing the rules
array to handle the TypeScript files. So, when webpack finds a file with the .ts
or .tsx
extension, it’ll use the ts-loader
to compile it. Only then will the file be added to the dist
build.
-
There are multiple ways to install PDF.js. Here, you’ll install it as a dependency in your
package.json
file:
npm install pdfjs-dist
pdfjs-dist
provides its own type definitions, so you don’t need to install any other dependency for the TypeScript support.
Rendering a PDF
After setting up your project with webpack and TypeScript, you can start interacting with the PDF.js library.
-
Create the entry point of your application by creating a
src
directory and anindex.html
file:
mkdir src && touch src/index.html
-
Create a
<canvas>
element, which you want the first page of the PDF to be rendered into:
<!DOCTYPE html> <html lang="en"> <head> <title>PDF.js Example</title> </head> <body> <canvas id="pdf"></canvas> </body> </html>
-
Now, create an
index.ts
file inside thesrc
directory:
.ts
is the extension used for TypeScript files. Later, you’ll run your code through the TypeScript transpiler to output a JavaScript (.js
) version of the file:
import * as pdfjsLib from "pdfjs-dist"; (async () => { const loadingTask = pdfjsLib.getDocument("example.pdf"); const pdf = await loadingTask.promise; // Load information from the first page. const page = await pdf.getPage(1); const scale = 1; const viewport = page.getViewport({ scale }); // Apply page dimensions to the `<canvas>` element. const canvas = document.getElementById("pdf") as HTMLCanvasElement; const context = canvas.getContext("2d"); canvas.height = viewport.height; canvas.width = viewport.width; // Render the page into the `<canvas>` element. const renderContext = { canvasContext: context, viewport: viewport, }; await page.render(renderContext); console.log("Page rendered!"); })();
Here, you’re explicitly importing everything from the pdfjs-dist
package and adding an interface for the HTMLCanvasElement
.
-
Use the
pdfjsLib.getDocument(pdf)
method to load the PDF file, with thepdf
parameter being the path of the PDF file. Then, use the.promise
method to handle the promise. -
From there, you can access a single page via the
page(pageNumber)
method (pageNumber
starts at 1 for the first page, and so on). -
To draw something on the canvas, use the
canvas.getContext()
method. This method returns a context object you can use to draw on the canvas. -
The
getViewport(scale)
method returns a viewport object that represents the page at the given scale. -
Use the viewport information to set the dimensions of the
<canvas>
element, and then start the page renderer with therender(options)
API.
Running the Project
-
To run the project, add some scripts to the
package.json
file:
"scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack.config.js", "prestart": "npm run build", "dev": "tsc", "start": "serve -l 8080 ./dist" },
-
Add your PDF file inside the
src
directory before running the project. You can use our demo document as an example. -
Now, run
npm start
to start the server. Navigate tohttp://localhost:8080
to see the contents of thedist
directory.
![]()
You can access the full code on GitHub.
Building a TypeScript PDF Viewer with PSPDFKit
We offer a commercial TypeScript PDF library that can easily be integrated into your web application. It comes with 30+ features that let you view, annotate, edit, and sign documents directly in your browser. Out of the box, it has a polished and flexible UI that you can extend or simplify based on your unique use case.
Getting Started
-
Create a new folder and change your directory to it:
mkdir typescript-pspdfkit-viewer cd typescript-pspdfkit-viewer
-
Similar to what you did above, create a
package.json
file by runningnpm init --yes
. -
Create a
tsconfig.json
file and use the following configuration:
{ "compilerOptions": { "removeComments": true, "preserveConstEnums": true, "module": "commonjs", "target": "es5", "sourceMap": true, "noImplicitAny": true, "esModuleInterop": true }, "include": ["src/**/*"] }
Installing PSPDFKit and Configuring webpack
-
Install the PSPDFKit for Web library as a dependency with
npm
oryarn
:
npm install pspdfkit
-
Install the necessary dependencies for webpack, create a
config
directory, and place yourwebpack
configuration file inside it:
mkdir config && touch config/webpack.js
-
If you’re using webpack 4, use the example file. If you’re using the latest version of webpack — currently
^5.72.0
— use the following configuration:
// webpack.js const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const filesToCopy = [ // PSPDFKit files. { from: './node_modules/pspdfkit/dist/pspdfkit-lib', to: './pspdfkit-lib', }, // Application CSS. { from: './src/index.css', to: './index.css', }, // Example PDF. { from: './assets/example.pdf', to: './example.pdf', }, ]; /** * webpack main configuration object. */ const config = { entry: path.resolve(__dirname, '../src/index.ts'), mode: 'development', devtool: 'inline-source-map', output: { path: path.resolve(__dirname, '../dist'), filename: '[name].js', }, resolve: { extensions: ['.ts', '.tsx', '.js'], }, module: { rules: [ // All files with a `.ts` or `.tsx` extension will be handled by `ts-loader`. { test: /\.tsx?$/, loader: 'ts-loader', exclude: /node_modules/, }, ], }, plugins: [ // Automatically insert <script src="[name].js"><script> into the page. new HtmlWebpackPlugin({ template: './src/index.html', }), // Copy the WASM/ASM and CSS files to the `output.path`. new CopyWebpackPlugin({ patterns: filesToCopy }), ], optimization: { splitChunks: { cacheGroups: { // Creates a `vendor.js` bundle that contains external libraries (including `pspdfkit.js`). vendor: { test: /node_modules/, chunks: 'initial', name: 'vendor', priority: 10, enforce: true, }, }, }, }, }; module.exports = config;
Displaying the PDF
-
Add the PDF document you want to display to the
assets
directory. You can use our demo document as an example. -
Create an
index.html
file inside thesrc
directory and add the following code:
<!DOCTYPE html> <html> <head> <title>PSPDFKit for Web — TypeScript example</title> <link rel="stylesheet" href="index.css" /> </head> <body> <div class="container"></div> </body> </html>
This adds an empty <div>
element to where PSPDFKit will be mounted.
-
Declare the height of this element in your CSS file like this:
.container {
height: 100vh;
}
-
Now, create an
index.ts
file inside thesrc
directory:
import PSPDFKit from 'pspdfkit'; function load(document: string) { console.log(`Loading ${document}...`); PSPDFKit.load({ document, container: '.container', }) .then((instance) => { console.log('PSPDFKit loaded', instance); }) .catch(console.error); } load('example.pdf');
Here, you’ve imported the PSPDFKit
library and created a function that loads the PDF document.
Running the Project
-
Now, write some scripts in the
package.json
file to start your server:
"scripts": { "build": "cross-env NODE_ENV=production webpack --config config/webpack.js", "prestart": "npm run build", "dev": "tsc", "start": "serve -l 8080 ./dist" },
-
Run
npm start
to start the server. Navigate tohttp://localhost:8080
to see the contents of thedist
directory.
Opening Different PDF Files
The current viewer is loading the PDF file you have in the assets/example.pdf
file. But you can also open different PDF files by adding an input field with the file
type on the index.html
file:
<body> <div> <input type="file" class="chooseFile" accept="application/pdf" /> </div> <br /> <div class="container"></div> </body>
Now, go to the index.ts
file and add the following code:
interface HTMLInputEvent extends Event { target: HTMLInputElement & EventTarget; } let objectUrl = ''; document.addEventListener('change', function (event: HTMLInputEvent) { if ( event.target && event.target.className === 'chooseFile' && event.target.files instanceof FileList ) { PSPDFKit.unload('.container'); if (objectUrl) { URL.revokeObjectURL(objectUrl); } objectUrl = URL.createObjectURL(event.target.files[0]); load(objectUrl); } });
Here, you’ve added a change
event listener to the <input>
element. When the user selects a file, you’ll unload the current PDF and load the new one.
![]()
If you want to learn how to add annotations to your application, check out the Open and Annotate PDFs in a TypeScript App blog post. You can see the example project with annotation support on GitHub.
Adding Even More Capabilities
Once you’ve deployed your viewer, you can start customizing it to meet your specific requirements or easily add more capabilities. To help you get started, here are some of our most popular TypeScript guides:
- Adding annotations
- Editing documents
- Filling PDF forms
- Adding signatures to documents
- Real-time collaboration
- Redaction
- UI customization
Conclusion
In this tutorial, you saw how to build a TypeScript PDF viewer — first with PDF.js, and then with the PSPDFKit TypeScript PDF viewer library that allows you to display and render PDF files in your TypeScript application.
We created similar how-to blog posts using different web frameworks and libraries:
- How to Build an Angular PDF Viewer with PDF.js
- How to Build a Vue.js PDF Viewer with PDF.js
- How to Build a React PDF Viewer with PDF.js
- How to Build a Bootstrap 5 PDF Viewer with PDF.js
- How to Build an Electron PDF Viewer with PDF.js
- How to Build a jQuery PDF Viewer with PDF.js
- How to Build a JavaScript PDF Viewer with PDF.js
To get started with our TypeScript PDF viewer, try it for free, or launch our web demo.