How to build an Angular PDF viewer with PDF.js

In this step-by-step tutorial, you’ll build a custom Angular PDF viewer using three different approaches. First, you’ll create a lightweight viewer using the open source PDF.js library. Then, you’ll streamline development with ngx-extended-pdf-viewer, which layers a full UI on top of PDF.js. Finally, you’ll explore how to integrate the enterprise-grade Nutrient Web SDK to unlock advanced features like annotations, form handling, and multi-format support. Whether you’re building a basic PDF viewer or a fully featured document solution, this guide has you covered.
How to build an Angular PDF viewer with PDF.js
TL;DR

This tutorial explores three Angular PDF viewer options: a basic PDF.js implementation requiring manual setup of rendering and navigation; ngx-extended-pdf-viewer, which offers a prebuilt interface with simplified integration; and Nutrient Web SDK, which provides advanced features like annotations and form filling. Choose PDF.js for basic viewing needs, ngx-extended-pdf-viewer for easier implementation, or Nutrient for production applications requiring comprehensive document handling capabilities.

Angular, developed by Google, is a powerful framework for building dynamic web applications, while PDF.js, an open source JavaScript library by Mozilla, enables seamless PDF rendering within browsers. This post will show how to combine these technologies to create a PDF viewer within an Angular app.

Why use PDF.js instead of native PDF viewing?

Most modern browsers, like Chrome and Firefox, include built-in PDF viewers that are sufficient for simple use cases — such as displaying static PDFs or allowing users to print documents. However, native viewers offer limited functionality and little-to-no flexibility for customization.

PDF.js, Mozilla’s open source JavaScript library, bridges that gap. It enables PDF rendering directly in the browser while providing control over how documents are displayed and interacted with. Here’s why PDF.js is a preferred choice for Angular applications:

  • Consistent cross-browser support — Works reliably across all modern browsers, unlike native viewers which vary in capability and behavior.
  • Interactive features — Supports zooming, page navigation, search, and rendering of complex PDF elements like forms, annotations, and embedded images.
  • Customizable UI — Gives developers full control over the viewer interface, enabling seamless integration with your app’s design and UX requirements.
  • Open source and extensible — Free to use, actively maintained, and easy to extend with custom features tailored to your specific needs.

If your application requires more than just viewing static PDFs — like advanced navigation, user interaction, or dynamic UI integration — PDF.js offers a powerful, flexible foundation to build on.

Open source and commercial Angular PDF viewer libraries

When integrating a PDF viewer into an Angular app, there are several options based on your needs and the features required. Here’s an overview of three popular libraries: PDF.js, ngx-extended-pdf-viewer, and Nutrient.

1. PDF.js

PDF.js is a lightweight, open source library designed for basic PDF viewing. It’s great for simple PDFs but requires additional effort for advanced features like UI customization or annotations.

2. ngx-extended-pdf-viewer

ngx-extended-pdf-viewer is a popular open source library built on top of PDF.js, offering a richer user interface and more advanced features right out of the box. It’s highly customizable, making it easy to tweak the viewer’s look and behavior. This library is ideal for developers who need both flexibility and an out-of-the-box solution.

Features

  • Built on PDF.js, with a prebuilt UI for zoom, page navigation, text selection, and more.
  • Highly customizable and well-documented.
  • Actively maintained, with great support from its developers.

3. Nutrient Web SDK

Nutrient is a commercial PDF viewer with a comprehensive feature set, ideal for enterprises and applications requiring advanced PDF functionalities.

Features

  • 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.

Requirements for implementing an Angular PDF viewer

To get started, you’ll need:

When you install Node.js, npm is installed by default.

Setup

Go to your terminal and install the Angular command-line interface (CLI)(opens in a new tab). This will help you get up and running quickly with Angular:

npm install -g @angular/cli
yarn global add @angular/cli

Now, you can check the version of Angular:

Terminal window
ng version

Integrate PDF.js into your Angular project

Setting up the Angular project

Start by setting up a new Angular project. If you already have an Angular project, you can skip this step.

  1. Open your terminal and run the following commands:
Terminal window
ng new angular-pdf-viewer
cd angular-pdf-viewer
  1. Next, install the pdfjs-dist package, which contains the PDF.js library:
Terminal window
npm install pdfjs-dist
  1. Configure the worker file:

PDF.js relies on a separate worker file for improved performance. You need to configure Angular to serve this worker file properly.

First, create the assets directory under the src folder if it doesn’t exist:

Terminal window
mkdir -p src/assets

Copy the pdf.worker.min.mjs file into the assets directory of your Angular project. You can use the following bash command:

Terminal window
cp node_modules/pdfjs-dist/build/pdf.worker.min.mjs src/assets/

Make sure to add your sample.pdf file (or any PDF file you want to render) to the assets directory. This will allow you to load the PDF from this path in the loadPdf method later on.

Your project structure should look like this:

Terminal window
angular-pdf-viewer/
├── src/
├── assets/
├── pdf.worker.min.mjs <-- Copied worker file
├── sample.pdf <-- Your sample PDF

Open your angular.json file and add the following to the "assets" array to include the PDF.js worker file:

"assets": [
"src/assets",
{
"glob": "pdf.worker.min.mjs",
"input": "node_modules/pdfjs-dist/build/",
"output": "/assets/"
}
],

This ensures the worker file is accessible in the application and can be used to offload processing tasks.

Creating the PDF viewer component

Now, you’ll create a new component where you’ll implement the PDF viewer. This component will contain all the logic for rendering the PDF and handling navigation and zoom functionality.

Run the following Angular CLI command to generate the component:

Terminal window
ng generate component pdf-viewer
  1. The next step is to create the HTML template and styles that define the layout of your PDF viewer.

The HTML template, pdf-viewer.component.html, includes buttons for navigation, zoom controls, and a container for displaying the PDF document:

<div class="pdf-viewer-controls">
<button (click)="goToPrevPage()" [disabled]="currentPage <= 1">
Previous
</button>
<span>Page {{ currentPage }} of {{ totalPages }}</span>
<button (click)="goToNextPage()" [disabled]="currentPage >= totalPages">
Next
</button>
<button (click)="zoomIn()">Zoom In</button>
<button (click)="zoomOut()">Zoom Out</button>
</div>
<div class="pdf-container">
<div #pdfContainer></div>
</div>
  • Navigation buttons — The Previous and Next buttons allow the user to navigate between PDF pages.
  • Zoom controls — The Zoom In and Zoom Ou buttons adjust the zoom level for the PDF.
  • PDF container — The #pdfContainer div is where the PDF pages will be rendered dynamically using the <canvas> element.

Adding the component logic

Now, you’ll implement the logic to load and render the PDF, handle navigation, and adjust zoom. This will be done in the TypeScript file of the component.

Implement the TypeScript logic with pdf-viewer.component.ts, like so:

import {
Component,
OnInit,
OnDestroy,
ElementRef,
ViewChild,
} from "@angular/core";
import * as pdfjsLib from "pdfjs-dist";
@Component({
selector: "app-pdf-viewer",
templateUrl: "./pdf-viewer.component.html",
styleUrls: ["./pdf-viewer.component.css"],
})
export class PdfViewerComponent implements OnInit, OnDestroy {
@ViewChild("pdfContainer", { static: true })
pdfContainer!: ElementRef<HTMLDivElement>;
private pdfDocument: any;
private currentPageNumber = 1;
private scale = 1.5;
totalPages = 0;
currentPage = 1;
constructor() {}
ngOnInit(): void {
this.loadPdf();
}
ngOnDestroy(): void {
// Clean up resources when the component is destroyed.
}
// Load the PDF file.
async loadPdf() {
try {
const pdfjs = pdfjsLib as any;
pdfjs.GlobalWorkerOptions.workerSrc = "assets/pdf.worker.min.mjs";
const loadingTask = pdfjs.getDocument("assets/sample.pdf"); // Path to your PDF file.
this.pdfDocument = await loadingTask.promise;
this.totalPages = this.pdfDocument.numPages;
this.renderPage(this.currentPageNumber);
} catch (error) {
console.error("Error loading PDF:", error);
}
}
// Render a specific page of the PDF.
async renderPage(pageNumber: number) {
const page = await this.pdfDocument.getPage(pageNumber);
const viewport = page.getViewport({ scale: this.scale });
const container = this.pdfContainer.nativeElement;
container.innerHTML = ""; // Clear previous content
const canvas = document.createElement("canvas");
container.appendChild(canvas);
const context = canvas.getContext("2d")!;
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext).promise;
}
// Navigate to the previous page.
goToPrevPage() {
if (this.currentPageNumber > 1) {
this.currentPageNumber--;
this.currentPage = this.currentPageNumber;
this.renderPage(this.currentPageNumber);
}
}
// Navigate to the next page.
goToNextPage() {
if (this.currentPageNumber < this.totalPages) {
this.currentPageNumber++;
this.currentPage = this.currentPageNumber;
this.renderPage(this.currentPageNumber);
}
}
// Zoom in to the PDF.
zoomIn() {
this.scale += 0.25;
this.renderPage(this.currentPageNumber);
}
// Zoom out of the PDF.
zoomOut() {
if (this.scale > 0.5) {
this.scale -= 0.25;
this.renderPage(this.currentPageNumber);
}
}
}
  • PDF loading — The loadPdf function uses the pdfjsLib.getDocument method to load the PDF document. You can replace 'assets/sample.pdf' with the URL or the path of any PDF you want to display.
  • Page rendering — The renderPage method renders the current page of the PDF to a <canvas> element. This method is called whenever the page changes or the zoom level is adjusted.
  • Navigation — The goToPrevPage and goToNextPage methods update the currentPageNumber and call renderPage to load the respective page.
  • Zoom — The zoomIn and zoomOut methods adjust the scale value, which in turn changes the zoom level of the rendered PDF page.

Styling the PDF viewer

Now you’ll add some basic styles to make the viewer user-friendly and visually appealing.

Add the styling (pdf-viewer.component.css):

.pdf-viewer-controls {
display: flex;
justify-content: center;
margin-bottom: 10px;
}
button {
margin: 0 5px;
padding: 5px 10px;
}
.pdf-container {
display: flex;
justify-content: center;
align-items: center;
}

Run the application

Start your Angular application by running:

Terminal window
ng serve

Open your browser and navigate to http://localhost:4200. You’ll see your PDF viewer with navigation controls to browse through the pages.

Access the project on GitHub(opens in a new tab).

Building an Angular PDF viewer with ngx-extended-pdf-viewer

Open your terminal and create a new Angular project in a fresh folder:

Terminal window
mkdir pdf-viewer && cd pdf-viewer
ng new ngx-pdf-viewer
  • When prompted, choose:
    • Routing — No
    • Stylesheet format — CSS

Then move into the new project directory and start the dev server:

Terminal window
cd ngx-pdf-viewer
npm start

The app should now be running at http://localhost:4200(opens in a new tab).

Adding ngx-extended-pdf-viewer

Run the command below to install the ngx-extended-pdf-viewer library via npm or yarn. This will install the latest version of the library:

npm install ngx-extended-pdf-viewer
yarn add ngx-extended-pdf-viewer

Now you need to configure the angular.json file. You’ll add this configuration under the projects > yourProjectName > architect > build > options > assets section:

"assets": [
"src/favicon.ico",
"src/assets",
{
"glob": "**/*",
"input": "node_modules/ngx-extended-pdf-viewer/assets/",
"output": "/assets/"
}
],

Go to the app.component.ts file, import NgxExtendedPdfViewerModule from ngx-extended-pdf-viewer, and pass it to the imports array:

src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, NgxExtendedPdfViewerModule],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})

Displaying a PDF

Add your PDF document to the src/assets directory. You can use our demo document as an example.

There’s one more step left to see the PDF file in the browser. Go to the app.component.html file and replace the contents of app.component.html with:

<ngx-extended-pdf-viewer
[src]="'assets/example.pdf'"
[textLayer]="true"
[showHandToolButton]="true"
[showPresentationModeButton]="true"
[showDownloadButton]="true"
></ngx-extended-pdf-viewer>

There are many configuration options you can use. You can see all the options(opens in a new tab) on the ngx-extended-pdf-viewer website.

Now, run your project with the following command:

Terminal window
ng serve

Navigate to localhost:4200 to see your PDF file.

Access the project on GitHub(opens in a new tab).

ngx-extended-pdf-viewer demo

Building an Angular PDF viewer with Nutrient

While ngx-extended-pdf-viewer is a solid open source option, it comes with some tradeoffs:

  • Frequent changes in the library can break your app unless you’re using the latest version.
  • Performance issues with large files (e.g. 1,000+ page documents) have been reported.

For a more robust, enterprise-ready solution, Nutrient provides a modern PDF viewing experience with enhanced performance, editing tools, and support for images and Office documents — all in one viewer.

The following section will walk through how to integrate Nutrient Web SDK into your Angular project.

Add PDF functionality with Angular using Nutrient Web SDK

Nutrient Web SDK is a fast, flexible JavaScript library for rendering and interacting with PDFs entirely in the browser. It requires no server-side rendering and includes features like form filling, dark mode, bookmarks, and responsive design.

Follow the steps below to integrate Nutrient into your Angular app and build a fully functional PDF viewer.

Step 1 — Install the Nutrient viewer

Install the SDK package using your preferred package manager:

Terminal window
npm install @nutrient-sdk/viewer

Then, configure Angular to copy the SDK’s required assets at build time. Open your angular.json file and add this to the assets array:

"assets": [
"src/assets",
{
"glob": "**/*",
"input": "./node_modules/@nutrient-sdk/viewer/dist/nutrient-viewer-lib/",
"output": "./assets/nutrient-viewer-lib/"
}
]

This ensures Nutrient’s JavaScript and WebAssembly files are bundled into your app so the SDK can load them when the viewer runs.

Step 2 — Create and configure the PDF viewer

  1. Use Angular CLI to create a new component:
Terminal window
ng generate component pdf-viewer

This creates the component’s HTML, TypeScript, and CSS files in src/app/pdf-viewer.

  1. In pdf-viewer.component.html, add a container where the viewer will mount:
<div class="pdf-viewer">
<div id="nutrient-container" style="width: 100%; height: 100vh"></div>
</div>

The #nutrient-container div is where the SDK will render the PDF viewer UI.

Load and render the PDF

In pdf-viewer.component.ts, initialize the SDK:

import { Component, OnInit } from "@angular/core";
import NutrientViewer from "@nutrient-sdk/viewer";
@Component({
selector: "pdf-viewer",
templateUrl: "./pdf-viewer.component.html",
styleUrls: ["./pdf-viewer.component.css"],
standalone: true,
})
export class PdfViewerComponent implements OnInit {
ngOnInit(): void {
NutrientViewer.load({
// The SDK loads viewer assets from this base URL.
baseUrl: `${location.protocol}//${location.host}/assets/`,
// PDF document to load.
document: "/assets/document.pdf",
// DOM container where the viewer should be rendered.
container: "#nutrient-container",
}).then((instance) => {
// Optional: expose viewer instance for debugging.
(window as any).instance = instance;
});
}
}

Add a sample PDF to the src/assets folder and name it document.pdf, or use any PDF file you want to display. Nutrient Web SDK will automatically load this file when the viewer initializes.

Step 3 — Display the viewer in your app

To make the viewer appear, import and use the PdfViewerComponent in your root component.

In app.component.ts:

import { Component } from "@angular/core";
import { RouterOutlet } from "@angular/router";
import { PdfViewerComponent } from "./pdf-viewer/pdf-viewer.component";
@Component({
selector: "app-root",
imports: [RouterOutlet, PdfViewerComponent],
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {
title = "angular";
}

In app.component.html:

<pdf-viewer></pdf-viewer>

This mounts your PDF viewer on the page using Angular’s component system.

Step 4 — Run the application

Start your Angular app with:

Terminal window
npm run dev

Navigate to http://localhost:4200 and you’ll see your PDF rendered inside the Nutrient viewer. The UI includes zoom, navigation, full-text search, annotation tools, and more — all ready to use.

Access the project on GitHub(opens in a new tab).

Video poster

Troubleshooting common issues

When integrating PDF.js into your Angular project, you might encounter some common issues. Here are some potential problems and their solutions.

1. PDF.js not rendering correctly

Issue: Sometimes, the PDF doesn’t render or displays incorrectly (blank or garbled).

Solution: Ensure that the pdf.worker.min.mjs file is correctly linked in the angular.json file and served from the correct location. Double-check the file path and the worker configuration in the loadPdf method.

2. Cross-origin resource sharing (CORS) errors

Issue: When loading PDFs from a different domain, you may get CORS errors.

Solution: You need to ensure that the server hosting the PDFs has CORS enabled, or you can load the PDFs from the same server as your Angular app.

3. PDF.js library loading issues

Issue: The library doesn’t load or gives an error such as “Unable to find PDF.js.”

Solution: Ensure you’ve correctly installed pdfjs-dist and the path to the worker file is accurate. If using a CDN for PDF.js, make sure the CDN is accessible.

Performance optimization techniques

Lazy loading of PDF documents

When dealing with large PDFs, it can be inefficient to load the entire document upfront. Implementing lazy loading of individual pages can significantly improve performance.

Here’s how you can implement lazy loading:

  • Load pages dynamically — Instead of loading all pages at once, load the page as the user navigates to it.
  • Preload adjacent pages — When navigating to a page, preload the next and previous pages in the background to avoid delays when flipping through pages.

Efficient memory management

To prevent memory leaks and improve performance, ensure each page is disposed of when it’s no longer needed. You can use page.cleanup() to clear resources when a page is no longer in view.

Handling large files

When dealing with large files, consider the following strategies:

  • Split the document — If possible, split large PDFs into smaller chunks and load them as needed.
  • Limit concurrent render operations — Ensure that only a few pages are rendered concurrently, especially on mobile devices with limited memory.

Enhancing user experience

Custom navigation controls

Implementing custom controls for PDF navigation can improve user experience. You can customize the "Previous" and "Next" buttons to offer more control, such as adding a jump-to-page feature or a slider for easy navigation through pages:

<!-- Add a custom page input -->
<input
type="number"
[(ngModel)]="currentPage"
(ngModelChange)="navigateToPage($event)"
[max]="totalPages"
[min]="1"
/>
// TypeScript logic for custom page navigation.
navigateToPage(pageNumber: number) {
if (pageNumber > 0 && pageNumber <= this.totalPages) {
this.currentPageNumber = pageNumber;
this.renderPage(this.currentPageNumber);
}
}

Responsive design

To ensure the PDF viewer is mobile-friendly, you can apply CSS media queries to make the viewer adjust to different screen sizes:

/* Responsive Design for PDF viewer */
@media (max-width: 600px) {
.pdf-viewer-controls {
flex-direction: column;
align-items: center;
}
.pdf-container {
max-width: 100%;
overflow: hidden;
}
}

Accessibility features

Adding accessibility features makes the PDF viewer usable for users with disabilities.

  1. Keyboard navigation — Ensure users can navigate through pages using the keyboard (e.g. Arrow Up/Down).

  2. Screen reader support — Provide proper alt text or aria-label attributes for buttons and elements:

<button
(click)="goToPrevPage()"
[aria-label]="'Go to previous page'"
[disabled]="currentPage <= 1"
>
Previous
</button>
  1. High contrast mode — Provide an option for high-contrast viewing modes for users with visual impairments:
/* High Contrast Mode */
.high-contrast-mode {
background-color: black;
color: white;
}

Conclusion

In this tutorial, you explored two popular approaches for building a PDF viewer in Angular — using the open source ngx-extended-pdf-viewer library, and using the commercial-grade Nutrient Web SDK.

For basic PDF viewing, open source tools like PDF.js or ngx-extended-pdf-viewer can get you started quickly. But when you need advanced functionality — like annotations, form handling, Office file support, or a polished UI — a commercial solution like Nutrient offers significant advantages.

Looking for a production-ready Angular PDF viewer that’s customizable, performant, and well-documented? Try Nutrient for free, or explore our live demo.

Explore more framework guides

Want to build a PDF viewer with a different tech stack? Check out our other tutorials:

FAQ

What are the key features of ngx-extended-pdf-viewer?

ngx-extended-pdf-viewer supports Angular 9-13, uses PDF.js, and is highly customizable with features like text layers, zoom controls, and more.

When should I use Nutrient instead of PDF.js?

Use Nutrient if you need advanced features like PDF editing, annotation tools, multi-format support, or improved performance.

What are the basic steps to integrate ngx-extended-pdf-viewer into an Angular project?

Install Angular CLI, create a new Angular project, add ngx-extended-pdf-viewer via npm or yarn, configure it in angular.json, and update app.component.html to display your PDF.

How do I add Nutrient to an Angular project?

Install Nutrient, configure angular.json to include its assets, and update app.component.html and app.component.ts to load and display the PDF using Nutrient.

Hulya Masharipov

Hulya Masharipov

Technical Writer

Hulya is a frontend web developer and technical writer at Nutrient who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

Explore related topics

FREE TRIAL Ready to get started?