In this tutorial, you’ll first learn how to set up a PDF viewer in Angular using the open source PDF.js library. Then, you’ll extend its functionality with ngx-extended-pdf-viewer for a richer user interface. Finally, you’ll enhance the viewer further by integrating the powerful Nutrient Web SDK for advanced features.

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.
Advantages of using PDF.js for an Angular PDF viewer
PDF.js, an open source JavaScript library by Mozilla, enables PDF rendering directly in the browser, offering several advantages when building an Angular PDF viewer. The library is robust, well-maintained, and capable of handling complex PDFs with features like zoom, pagination, and text search. Here are some of its key benefits:
-
Comprehensive feature set — PDF.js is ideal for handling various PDF elements, including text, images, and annotations, with basic support for features like zooming, page navigation, and printing.
-
Cross-browser compatibility — Works across all modern browsers, ensuring consistent rendering regardless of the user’s platform.
-
Extensibility — PDF.js is highly customizable, enabling developers to add advanced features such as custom navigation, rendering options, and interactions with the document.
-
Open source — Because it’s open source, PDF.js is free to use and can be extended or modified to fit specific needs, making it a cost-effective solution.
While PDF.js is robust, it is important to understand the scenarios where its features are most beneficial, and when simpler, native browser capabilities may suffice.
When native browser PDF capabilities are sufficient
For many use cases, native browser PDF support is enough. Browsers like Chrome and Firefox already provide PDF viewing functionality, and in some cases, integrating PDF.js may be overkill. Here are scenarios where native PDF rendering can be a viable choice:
-
Displaying simple PDFs — If your application only needs to display static PDFs without interactive elements, native PDF rendering might be the best choice.
-
Reducing dependencies — Using native browser features reduces your app’s reliance on external libraries, making it simpler to manage and potentially more performant for lightweight use cases.
-
Quick and basic viewing needs — For apps where users only need basic functionality like viewing or printing PDFs, relying on the built-in PDF viewer may be more efficient than integrating an external library.
Why choose PDF.js over native solutions
While native browser capabilities are useful for simple PDF viewing, PDF.js becomes the go-to option for more advanced features, specifically for applications that require:
-
Interactive features — Features like zooming, navigating between pages, or adding form fields may not be well-supported by native solutions.
-
Customizability — Native PDF viewers often lack the flexibility to customize the viewer’s user interface (UI) or behavior according to specific app needs.
-
Advanced document handling — PDF.js can handle complex documents with hyperlinks, and large file sizes, which native viewers might struggle with or fail to adequately support.
Thus, PDF.js is especially advantageous when your app needs to offer more control over the PDF viewing experience or handle large, interactive documents that go beyond basic viewing.
Native browser PDF vs. PDF.js: A comparison
Here’s a quick comparison of the native browser PDF capabilities and PDF.js, highlighting where each approach excels.
Feature | Native browser PDF | PDF.js |
---|---|---|
Simple PDF viewing | Yes | Yes |
Interactive features | Limited | Full support (forms, etc.) |
Customizable UI | No | Yes |
Cross-browser compatibility | Limited (depends on the browser) | Universal support across all modern browsers |
Complex document handling | No (struggles with large files or complex documents) | Yes, handles complex documents smoothly |
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
As discussed earlier, 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
Nutrient is a commercial PDF viewer with a comprehensive feature set, ideal for enterprises and applications requiring advanced PDF functionalities. It offers a polished, ready-to-use UI, as well as powerful features like annotations, form filling, and digital signatures. It also supports a wide range of file types, such as PDFs, MS Office documents, and images.
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.
Each of these solutions offers a different level of complexity and functionality. For simple applications with basic PDF viewing needs, PDF.js or ngx-extended-pdf-viewer are excellent open source choices. However, if your app requires advanced PDF manipulation, annotations, or enterprise-level support, Nutrient offers a feature-rich, professional solution.
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). 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:
ng version
How to integrate PDF.js in Angular
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.
-
Create a new Angular project:
Open your terminal and run the following commands:
ng new angular-pdf-viewer cd angular-pdf-viewer
-
Install PDF.js:
Next, install the pdfjs-dist
package, which contains the PDF.js library:
npm install pdfjs-dist
-
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:
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:
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:
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.
-
Generate the PDF viewer component:
Run the following Angular CLI command to generate the component:
ng generate component pdf-viewer
Designing the PDF viewer interface
The next step is to create the HTML template and styles that define the layout of your PDF viewer.
HTML template
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 Out” 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.
TypeScript logic
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 thepdfjsLib.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
andgoToNextPage
methods update thecurrentPageNumber
and callrenderPage
to load the respective page. -
Zoom — The
zoomIn
andzoomOut
methods adjust thescale
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.
Styling
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; }
Advanced features (optional)
While the implementation above provides basic PDF viewing functionality, there are several ways to enhance the viewer:
-
Adding a zoom slider — Instead of using buttons for zooming, you can add a slider for more precise control over the zoom level.
-
Search functionality — You can implement a search feature that allows users to find text within the PDF.
Run the application
Start your Angular application by running:
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.
Building an Angular PDF viewer with ngx-extended-pdf-viewer
Now, open your favorite integrated development environment (IDE). This demo uses Visual Studio Code. Navigate to the directory where you want your new Angular project to be. We created a directory named pdf-viewer
:
mkdir pdf-viewer
Change your directory into pdf-viewer
with cd pdf-viewer
.
Go to the project terminal and create a new Angular project:
ng new ngx-pdf-viewer
Choose No
for adding Angular routing and CSS
for the stylesheet.
Change your directory into the newly created folder:
cd ngx-pdf-viewer
Run the project on a server. This command will serve the Angular project:
npm start
yarn start
The project is running on localhost:4200
.
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 on the ngx-extended-pdf-viewer
website.
Now, run your project with the following command:
ng serve
Navigate to localhost:4200
to see your PDF file.
![]()
Access the project on GitHub.
Building an Angular PDF viewer with Nutrient
ngx-extended-pdf-viewer
is a great open source project, but it does have some disadvantages:
- The library changes regularly and can result in your app breaking at unexpected times. Ensure you’re running the most up-to-date version to avoid these disruptions.
- Some users report slow performance when previewing documents with more than 1,000 pages.
Nutrient offers a powerful PDF library that can be used to build your own Angular PDF viewer. On top of all the features you get with an open source library, some additional features you get with Nutrient include:
- Improved rendering performance
- PDF editing and annotating
- Image and MS Office file viewing
- Powerful document search
- A rich bookmark UI that enables you to add, remove, and sort bookmarks
- Dark mode support
- Responsive design
- PDF form viewing and designing
- And much more
You can integrate it into your existing or new Angular projects in a couple of steps.
Note on the assets directory
In recent Angular setups, especially when creating a new project, you might notice there isn’t an assets
directory created by default. This is often due to changes in Angular’s project structure in newer versions. If you don’t have this directory, create it under src
:
mkdir src/assets
Now, continue with the tutorial to see how to integrate Nutrient into your Angular project.
Stay in the same directory (pdf-viewer
) and create a new Angular project for Nutrient integration:
ng new web-example-angular
This will ask some configuration questions. Again, choose No
for routing and CSS
for the stylesheet. Now, change your directory to this project:
cd pspdfkit-web-example-angular
Adding Nutrient
Install pspdfkit
as a dependency with npm
or yarn
:
npm install pspdfkit
yarn add pspdfkit
Now, add the following to your angular.json
file. Angular will copy the Nutrient library assets to the assets
directory before running your app:
"assets": [ "src/favicon.ico", "src/assets", { "glob": "**/*", "input": "./node_modules/pspdfkit/dist/pspdfkit-lib/", "output": "./assets/pspdfkit-lib/" } ]
Displaying the PDF
Add the PDF document you want to display to the src/assets
directory. You can use the same file as in the ngx-extended-pdf-viewer demo as an example.
Replace the contents of app.component.html
with:
<div class="app"> <div class="toolbar"> <img class="logo" src="/favicon.ico" height="32" /> PSPDFKit Angular Application </div> <!-- We'll mount the PSPDFKit UI to this element. --> <div class="pspdfkit-container"></div> </div>
Replace the contents of app.component.ts
with the following:
import { Component } from "@angular/core"; import PSPDFKit from "pspdfkit"; @Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: ["app.component.css"], standalone: true, }) export class AppComponent { title = "PSPDFKit for Web Angular Example"; ngAfterViewInit(): void { PSPDFKit.load({ // Use the assets directory URL as a base URL. Nutrient will download its library assets from here. baseUrl: location.protocol + "//" + location.host + "/assets/", document: "/assets/example.pdf", container: ".pspdfkit-container", licenseKey: "YOUR_LICENSE_KEY_GOES_HERE", // optional license key }).then((instance) => { // For the sake of this demo, store the Nutrient Web SDK instance // on the global object so that you can open the dev tools and // play with the Nutrient API. (<any>window).instance = instance; }); } }
The license key is optional; however, you may see a watermark on your PDF files without a key. To get a key, contact Sales.
If you try to run your project, you may get an error stating the mounting container has no height. To fix this issue, add the following styles to the src/app/app.component.css
file:
:host { height: 100%; } .app { position: fixed; width: 100%; height: 100%; top: 0; right: 0; bottom: 0; left: 0; } .toolbar { position: relative; display: flex; align-items: center; height: 64px; width: 100%; padding: 0 24px; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); font-family: sans-serif; font-size: 20px; font-weight: 500; color: rgba(0, 0, 0, 0.8); } .logo { margin-right: 20px; } .pspdfkit-container { height: calc(100% - 64px); }
![]()
Access the project on GitHub.
Start the app and open it in your default browser:
npm start
yarn start
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.
-
Keyboard navigation — Ensure users can navigate through pages using the keyboard (e.g. Arrow Up/Down).
-
Screen reader support — Provide proper
alt
text oraria-label
attributes for buttons and elements:
<button (click)="goToPrevPage()" [aria-label]="'Go to previous page'" [disabled]="currentPage <= 1" > Previous </button>
-
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 article, you first looked at how to build a PDF viewer with the ngx-extended-pdf-viewer
library. In the second part of the article, you learned how to deploy the Nutrient Angular PDF viewer.
For simple use cases where the primary objective is viewing PDF documents, PDF.js offers a great low-cost solution. For more complex use cases, a commercial PDF viewer can provide some additional benefits:
- An out-of-the-box UI to help speed up development time. Quickly deploy a polished UI in your application and use well-documented APIs to customize the design and layout.
- Embed prebuilt tools to easily add functionality like annotating documents, editing PDFs, adding digital signatures to a PDF form, and much more.
- View multiple file types — from image files (JPG, PNG, TIFF) to MS Office documents.
- Get a quick response from a dedicated support team if you encounter a challenge or issue when integrating the viewer.
At Nutrient, we offer a commercial, feature-rich, and completely customizable Angular PDF library 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.
We created similar how-to blog posts using different web frameworks and libraries:
- How to build a React PDF viewer with PDF.js
- How to build a Vue.js PDF viewer with PDF.js
- How to build a jQuery 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 TypeScript PDF viewer with PDF.js
- How to build a JavaScript PDF viewer with PDF.js
FAQ
Below are some frequently asked questions about building an Angular PDF viewer.
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, addngx-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, configureangular.json
to include its assets, and update app.component.html
and app.component.ts
to load and display the PDF using Nutrient.