The most comprehensive Angular PDF viewer powered by Mozilla PDF.js. 7M+ downloads, mobile-first, production-ready with complete rewrite in v25.x
The most reliable, feature-rich Angular PDF viewer component powered by Mozilla's PDF.js
Live Demo β’ Documentation β’ API Reference β’ Examples β’ Server Examples β’ Migration Guide
ng2-pdfjs-viewer is the most comprehensive and feature-rich Angular PDF viewer component available. Born in 2018 and still going strong with over 7+ million downloads, this battle-tested library has been trusted by developers worldwide for over 8 years, powering thousands of applications.
This powerful library enables developers to seamlessly integrate PDF viewing capabilities into Angular applications with enterprise-grade features, custom theming, and mobile-first responsive design. Built on Mozilla's PDF.js v5.3.93, ng2-pdfjs-viewer provides advanced PDF rendering, document navigation, search functionality, and extensive customization options.
Whether you need a simple embedded PDF viewer or a complex document management system, this component delivers the performance and flexibility required for modern Angular applications. The most mature and reliable Angular PDF viewer solution with continuous updates and long-term support.
| Feature | Description | Status |
|---|---|---|
| Advanced Theme System | CSS custom properties for complete visual customization | β New |
| Template-Based UI | Custom loading spinners and error displays using Angular templates | β New |
| Convenience Configuration | Object-based configuration for cleaner, more maintainable code | β New |
| Event-Driven Architecture | Pure event-based system with universal action dispatcher | β New |
| Enhanced Error Handling | Multiple error display styles with custom templates | β New |
| Mobile-First Design | Responsive layout optimized for touch devices | β New |
| TypeScript Strict Mode | Full type safety with comprehensive API coverage | β New |
| URL Security Validation | Prevents unauthorized file parameter manipulation with custom templates | β New |
π― Live Demo: https://angular-pdf-viewer-demo.vercel.app/
π Documentation: https://angular-pdf-viewer-docs.vercel.app/
π Source Code: https://github.com/intbot/ng2-pdfjs-viewer/tree/main/SampleApp
npm install ng2-pdfjs-viewer --save
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { PdfJsViewerModule } from "ng2-pdfjs-viewer";
@NgModule({
imports: [BrowserModule, PdfJsViewerModule],
// ... rest of your module
})
export class AppModule {}
<ng2-pdfjs-viewer
pdfSrc="assets/sample.pdf"
[showSpinner]="true"
[theme]="'light'"
>
</ng2-pdfjs-viewer>
Add PDF.js assets to your angular.json:
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"assets": [
{
"glob": "**/*",
"input": "node_modules/ng2-pdfjs-viewer/pdfjs",
"output": "/assets/pdfjs"
}
]
}
}
}
}
}
}
π 7+ Million Downloads & Counting! - Trusted by developers worldwide for reliable PDF viewing in Angular applications since 2018.
style-src 'self')For production deployments using nginx, configure MIME types for PDF.js ES modules:
# Add to your nginx.conf or site configuration
types {
application/javascript js mjs;
text/plain ftl;
}
For production deployments using IIS, add to your web.config:
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".mjs" mimeType="application/javascript" />
<mimeMap fileExtension=".ftl" mimeType="text/plain" />
</staticContent>
</system.webServer>
</configuration>
Why this is needed: PDF.js v5+ uses .mjs files (ES modules) and .ftl files (localization). Without proper MIME type configuration, web servers serve these files with incorrect content-type headers, causing the PDF viewer to crash during loading in production environments.
| Angular Version | Support Level | Notes |
|---|---|---|
| 20.0+ | β Recommended | Fully tested and optimized |
| 15.0 - 19.x | β Supported | Should work with minor testing |
| 10.0 - 14.x | β Supported | Compatible with testing |
| 2.0 - 9.x | β οΈ Legacy | May require additional testing |
Note: While the library supports Angular 2.0+, it's primarily tested and optimized for Angular 20+. For production use with older versions, thorough testing is recommended. The library uses relaxed peer dependencies (Angular >=10.0.0) to ensure compatibility across different Angular versions.
# Using npm
npm install ng2-pdfjs-viewer --save
# Using yarn
yarn add ng2-pdfjs-viewer
# Using pnpm
pnpm add ng2-pdfjs-viewer
Add PDF.js assets to your angular.json:
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"assets": [
{
"glob": "**/*",
"input": "node_modules/ng2-pdfjs-viewer/pdfjs",
"output": "/assets/pdfjs"
}
]
}
}
}
}
}
}
<ng2-pdfjs-viewer pdfSrc="assets/document.pdf" [showSpinner]="true">
</ng2-pdfjs-viewer>
<ng2-pdfjs-viewer
pdfSrc="assets/document.pdf"
[theme]="'dark'"
[primaryColor]="'#007acc'"
[showSpinner]="true"
[customSpinnerTpl]="customSpinner"
[customErrorTpl]="customError"
(onDocumentLoad)="onDocumentLoaded($event)"
(onPageChange)="onPageChanged($event)"
>
</ng2-pdfjs-viewer>
<!-- Basic security (default enabled) -->
<ng2-pdfjs-viewer
pdfSrc="assets/document.pdf"
[urlValidation]="true">
</ng2-pdfjs-viewer>
<!-- Custom security template -->
<ng2-pdfjs-viewer
pdfSrc="assets/document.pdf"
[urlValidation]="true"
[customSecurityTpl]="securityTemplate">
</ng2-pdfjs-viewer>
<ng-template #securityTemplate let-warning="securityWarning">
<div class="alert alert-warning" *ngIf="warning">
<h4>β οΈ Security Warning</h4>
<p>{{ warning.message }}</p>
<button (click)="pdfViewer.dismissSecurityWarning()">Dismiss</button>
</div>
</ng-template>
import { Component, ViewChild } from "@angular/core";
import { PdfJsViewerComponent } from "ng2-pdfjs-viewer";
@Component({
template: `
<ng2-pdfjs-viewer #pdfViewer pdfSrc="document.pdf"></ng2-pdfjs-viewer>
<button (click)="goToPage(5)">Go to Page 5</button>
`,
})
export class MyComponent {
@ViewChild("pdfViewer") pdfViewer!: PdfJsViewerComponent;
async goToPage(page: number) {
await this.pdfViewer.goToPage(page);
}
}
// Component
export class MyComponent {
themeConfig = {
theme: "light",
primaryColor: "#007acc",
backgroundColor: "#ffffff",
toolbarColor: "#f5f5f5",
textColor: "#333333",
borderRadius: "8px",
};
}
<!-- Template -->
<ng2-pdfjs-viewer [themeConfig]="themeConfig" [customCSS]="customStyles">
</ng2-pdfjs-viewer>
<ng-template #customSpinner>
<div class="custom-spinner">
<div class="spinner"></div>
<p>Loading PDF...</p>
</div>
</ng-template>
<ng2-pdfjs-viewer
[customSpinnerTpl]="customSpinner"
[spinnerClass]="'my-spinner'"
>
</ng2-pdfjs-viewer>
<ng-template #customError>
<div class="error-container">
<mat-icon>error</mat-icon>
<h3>Failed to Load PDF</h3>
<p>Please check your internet connection and try again.</p>
<button mat-button (click)="retry()">Retry</button>
</div>
</ng-template>
<ng2-pdfjs-viewer [customErrorTpl]="customError" [errorClass]="'my-error'">
</ng2-pdfjs-viewer>
<!-- Basic external window (reuses same tab) -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf"
[externalWindow]="true">
</ng2-pdfjs-viewer>
<!-- Custom window options -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf"
[externalWindow]="true"
[externalWindowOptions]="'width=1200,height=800,scrollbars=yes,resizable=yes'">
</ng2-pdfjs-viewer>
<!-- Force new tab each time -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf"
[externalWindow]="true"
[target]="'pdf-viewer-' + Date.now()">
</ng2-pdfjs-viewer>
Tab Reuse Behavior:
target name β Reuses existing tab (default behavior)target name β Always opens new tabtarget="_blank" β Browser decides (usually reuses)<!-- Built-in security (always enabled) -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf">
</ng2-pdfjs-viewer>
Built-in Security Features:
allow-forms allow-scripts allow-same-origin allow-modalsSandbox Attributes (Fixed for Security):
allow-forms - Required for PDF form functionalityallow-scripts - Required for PDF.js JavaScript executionallow-same-origin - Required for loading PDF files and assetsallow-modals - Required for PDF.js dialogs (print, download)<!-- Default (no border) -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf">
</ng2-pdfjs-viewer>
<!-- Custom border -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf"
iframeBorder="2px solid #ccc">
</ng2-pdfjs-viewer>
<!-- Numeric border -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf"
[iframeBorder]="1">
</ng2-pdfjs-viewer>
<!-- No border (explicit) -->
<ng2-pdfjs-viewer
pdfSrc="document.pdf"
iframeBorder="0">
</ng2-pdfjs-viewer>
| Property | Type | Default | Description |
|---|---|---|---|
pdfSrc | string | Blob | Uint8Array | - | PDF source (URL, Blob, or byte array) |
viewerId | string | auto | Unique viewer identifier |
viewerFolder | string | 'assets' | Path to PDF.js assets |
externalWindow | boolean | false | Open in new window |
externalWindowOptions | string | - | External window options (size, position, etc.) |
target | string | '_blank' | Target for external window (controls tab reuse) |
theme | 'light' | 'dark' | 'auto' | 'light' | Theme selection |
primaryColor | string | '#007acc' | Primary color for UI elements |
backgroundColor | string | '#ffffff' | Background color |
pageBorderColor | string | - | Page border color |
toolbarColor | string | - | Toolbar background color |
textColor | string | - | Text color |
borderRadius | string | - | Border radius |
customCSS | string | - | Custom CSS styles |
cspNonce | string | - | CSP nonce for customCSS (optional) |
iframeTitle | string | - | Accessible title for iframe (optional) |
showSpinner | boolean | true | Show loading spinner |
customSpinnerTpl | TemplateRef | - | Custom spinner template |
spinnerClass | string | - | Custom spinner CSS class |
customErrorTpl | TemplateRef | - | Custom error template |
errorClass | string | - | Custom error CSS class |
errorOverride | boolean | false | Override default error handling |
errorAppend | boolean | true | Append to default error messages |
errorMessage | string | - | Custom error message |
locale | string | 'en-US' | UI language |
useOnlyCssZoom | boolean | false | Use CSS-based zoom for mobile |
diagnosticLogs | boolean | false | Enable diagnostic logging |
zoom | string | 'auto' | Initial zoom level (two-way binding) |
page | number | 1 | Initial page number |
namedDest | string | - | Named destination to navigate to |
cursor | string | 'select' | Cursor type (two-way binding) |
scroll | string | 'vertical' | Scroll mode (two-way binding) |
spread | string | 'none' | Spread mode (two-way binding) |
pageMode | string | 'none' | Page mode (two-way binding) |
rotation | number | 0 | Document rotation (two-way binding) |
showOpenFile | boolean | true | Show open file button |
showDownload | boolean | true | Show download button |
showPrint | boolean | true | Show print button |
showFind | boolean | true | Show search button |
showFullScreen | boolean | true | Show fullscreen button |
showViewBookmark | boolean | true | Show bookmark button |
showAnnotations | boolean | false | Show annotations |
showToolbarLeft | boolean | true | Show left toolbar section |
showToolbarMiddle | boolean | true | Show middle toolbar section |
showToolbarRight | boolean | true | Show right toolbar section |
showSecondaryToolbarToggle | boolean | true | Show secondary toolbar toggle |
showSidebar | boolean | true | Show sidebar |
showSidebarLeft | boolean | true | Show left sidebar |
showSidebarRight | boolean | true | Show right sidebar |
toolbarDensity | 'compact' | 'default' | 'comfortable' | 'default' | Toolbar density |
sidebarWidth | string | - | Sidebar width (e.g., '280px') |
toolbarPosition | 'top' | 'bottom' | 'top' | Toolbar position |
sidebarPosition | 'left' | 'right' | 'left' | Sidebar position |
responsiveBreakpoint | string | number | - | Responsive breakpoint |
downloadOnLoad | boolean | false | Auto-download on load |
printOnLoad | boolean | false | Auto-print on load |
rotateCW | boolean | false | Rotate clockwise on load |
rotateCCW | boolean | false | Rotate counter-clockwise on load |
showLastPageOnLoad | boolean | false | Go to last page on load |
downloadFileName | string | - | Custom download filename |
controlVisibility | ControlVisibilityConfig | - | Control visibility configuration |
autoActions | AutoActionConfig | - | Auto actions configuration |
errorHandling | ErrorConfig | - | Error handling configuration |
viewerConfig | ViewerConfig | - | Viewer configuration |
themeConfig | ThemeConfig | - | Theme configuration |
groupVisibility | GroupVisibilityConfig | - | Group visibility configuration |
layoutConfig | LayoutConfig | - | Layout configuration |
urlValidation | boolean | true | Enable URL validation |
customSecurityTpl | TemplateRef<any> | - | Custom security template |
securityWarning | SecurityWarning | null | - | Security warning data (read-only) |
iframeBorder | string | number | "0" | iframe border style |
| Event | Type | Description |
|---|---|---|
onDocumentLoad | EventEmitter<void> | Fired when document loads |
onDocumentInit | EventEmitter<void> | Fired when document initializes |
onDocumentError | EventEmitter<DocumentError> | Fired when document fails to load |
onPageChange | EventEmitter<ChangedPage> | Fired when page changes |
onPagesInit | EventEmitter<PagesInfo> | Fired when pages are initialized |
onPageRendered | EventEmitter<PageRenderInfo> | Fired when a page is rendered |
onScaleChange | EventEmitter<ChangedScale> | Fired when zoom/scale changes |
onRotationChange | EventEmitter<ChangedRotation> | Fired when rotation changes |
onPresentationModeChanged | EventEmitter<PresentationMode> | Fired when presentation mode changes |
onOpenFile | EventEmitter<void> | Fired when open file is clicked |
onFind | EventEmitter<FindOperation> | Fired when search is performed |
onUpdateFindMatchesCount | EventEmitter<FindMatchesCount> | Fired when search matches are updated |
onMetadataLoaded | EventEmitter<DocumentMetadata> | Fired when document metadata loads |
onOutlineLoaded | EventEmitter<DocumentOutline> | Fired when document outline loads |
onAnnotationLayerRendered | EventEmitter<AnnotationLayerRenderEvent> | Fired when annotation layer renders |
onBookmarkClick | EventEmitter<BookmarkClick> | Fired when bookmark is clicked |
onIdle | EventEmitter<void> | Fired when viewer becomes idle |
onBeforePrint | EventEmitter<void> | Fired before printing |
onAfterPrint | EventEmitter<void> | Fired after printing |
zoomChange | EventEmitter<string> | Fired when zoom changes (two-way binding) |
cursorChange | EventEmitter<string> | Fired when cursor changes (two-way binding) |
scrollChange | EventEmitter<string> | Fired when scroll changes (two-way binding) |
spreadChange | EventEmitter<string> | Fired when spread changes (two-way binding) |
pageModeChange | EventEmitter<string> | Fired when page mode changes (two-way binding) |
| Method | Parameters | Returns | Description |
|---|---|---|---|
refresh() | - | void | Refresh viewer |
goToPage(page: number) | page: number | Promise<ActionExecutionResult> | Navigate to specific page |
setPage(page: number) | page: number | Promise<ActionExecutionResult> | Set current page |
setZoom(zoom: string) | zoom: string | Promise<ActionExecutionResult> | Set zoom level |
setCursor(cursor: string) | cursor: 'select' | 'hand' | 'zoom' | Promise<ActionExecutionResult> | Set cursor type |
setScroll(scroll: string) | scroll: 'vertical' | 'horizontal' | 'wrapped' | 'page' | Promise<ActionExecutionResult> | Set scroll mode |
setSpread(spread: string) | spread: 'none' | 'odd' | 'even' | Promise<ActionExecutionResult> | Set spread mode |
setPageMode(mode: string) | mode: 'none' | 'thumbs' | 'bookmarks' | 'attachments' | Promise<ActionExecutionResult> | Set page mode |
triggerDownload() | - | Promise<ActionExecutionResult> | Trigger download |
triggerPrint() | - | Promise<ActionExecutionResult> | Trigger print |
triggerRotation(direction: string) | direction: 'cw' | 'ccw' | Promise<ActionExecutionResult> | Rotate document |
goToLastPage() | - | Promise<ActionExecutionResult> | Navigate to last page |
sendViewerControlMessage(action: string, payload: any) | action: string, payload: any | Promise<any> | Send custom control message |
getActionStatus(actionId: string) | actionId: string | Promise<ActionExecutionResult | null> | Get action status |
getQueueStatus() | - | { queuedActions: number; executedActions: number } | Get queue status |
clearActionQueue() | - | void | Clear action queue |
reloadViewer() | - | void | Reload viewer (alias for refresh) |
goBack() | - | void | Go back in browser history |
closeViewer() | - | void | Close viewer window |
getErrorTemplateData() | - | any | Get error template data |
setUrlValidation(enabled: boolean) | enabled: boolean | Promise<ActionExecutionResult> | Enable/disable URL validation |
dismissSecurityWarning() | - | void | Dismiss security warning |
π― Live Demo: https://angular-pdf-viewer-demo.vercel.app/
π Documentation: https://angular-pdf-viewer-docs.vercel.app/
π Source Code: https://github.com/intbot/ng2-pdfjs-viewer/tree/main/SampleApp
<ng2-pdfjs-viewer pdfSrc="assets/sample.pdf" [showSpinner]="true">
</ng2-pdfjs-viewer>
<ng2-pdfjs-viewer
pdfSrc="assets/sample.pdf"
[theme]="'dark'"
[primaryColor]="'#ff6b6b'"
[backgroundColor]="'#2c3e50'"
>
</ng2-pdfjs-viewer>
<ng-template #loadingTemplate>
<div class="loading">
<mat-spinner></mat-spinner>
<p>Loading your document...</p>
</div>
</ng-template>
<ng-template #errorTemplate>
<div class="error">
<mat-icon>error_outline</mat-icon>
<h3>Oops! Something went wrong</h3>
<p>We couldn't load your PDF. Please try again.</p>
<button mat-button (click)="retry()">Retry</button>
</div>
</ng-template>
<ng2-pdfjs-viewer
pdfSrc="assets/sample.pdf"
[customSpinnerTpl]="loadingTemplate"
[customErrorTpl]="errorTemplate"
>
</ng2-pdfjs-viewer>
export class PdfController {
// Group visibility configuration
groupVisibility = {
"download": true,
"print": true,
"find": true,
"fullScreen": true,
"openFile": true,
"viewBookmark": true,
"annotations": false
};
// Auto actions configuration
autoActions = {
"downloadOnLoad": false,
"printOnLoad": false
};
// Control visibility configuration
controlVisibility = {
"showToolbarLeft": true,
"showToolbarMiddle": true,
"showToolbarRight": true,
"showSecondaryToolbarToggle": true,
"showSidebar": true,
"showSidebarLeft": true,
"showSidebarRight": true
};
}
<ng2-pdfjs-viewer
pdfSrc="assets/sample.pdf"
[groupVisibility]="groupVisibility"
[autoActions]="autoActions"
[controlVisibility]="controlVisibility"
>
</ng2-pdfjs-viewer>
export class PdfController {
@ViewChild("pdfViewer") pdfViewer!: PdfJsViewerComponent;
async loadDocument(url: string) {
this.pdfViewer.pdfSrc = url;
await this.pdfViewer.refresh();
}
async goToPage(page: number) {
await this.pdfViewer.goToPage(page);
}
async setZoom(zoom: string) {
await this.pdfViewer.setZoom(zoom);
}
}
For server-side developers, we provide comprehensive examples for integrating PDF APIs with ng2-pdfjs-viewer:
π Server-Side Examples - Complete examples for:
Each example includes proper Content-Type headers, error handling, CORS configuration, and Angular integration patterns.
Update Dependencies
npm install ng2-pdfjs-viewer@latest
Update Theme Configuration
// Old way
[customCSS]="'body { background: red; }'"
// New way
[theme]="'light'"
[primaryColor]="'#ff0000'"
[backgroundColor]="'#ffffff'"
Update Error Handling
<!-- Old way -->
[errorHtml]="'<div>Custom error</div>'"
<!-- New way -->
<ng-template #errorTemplate>
<div>Custom error</div>
</ng-template>
<ng2-pdfjs-viewer [customErrorTpl]="errorTemplate"></ng2-pdfjs-viewer>
Update Spinner Handling
<!-- Old way -->
[spinnerHtml]="'<div class=\"spinner\">Loading...</div>'"
<!-- New way -->
<ng-template #spinnerTemplate>
<div class="spinner">Loading...</div>
</ng-template>
<ng2-pdfjs-viewer [customSpinnerTpl]="spinnerTemplate"></ng2-pdfjs-viewer>
// Old way
this.pdfViewer.setSpinnerHtml('<div>Loading...</div>');
// New way
// Use [customSpinnerTpl] with ng-template
The following features are deprecated and will be removed in future versions:
| Deprecated | Replacement | Description |
|---|---|---|
[startDownload] | [downloadOnLoad] | Download document automatically when it opens |
[startPrint] | [printOnLoad] | Print document automatically when it opens |
[errorHtml] | [customErrorTpl] | Custom error HTML (use template instead) |
[errorTemplate] | [customErrorTpl] | Custom error template (renamed) |
[spinnerHtml] | [customSpinnerTpl] | Custom spinner HTML (use template instead) |
| Deprecated | Replacement | Description |
|---|---|---|
setErrorHtml() | Use [customErrorTpl] | Set custom error HTML (use template instead) |
setSpinnerHtml() | Use [customSpinnerTpl] | Set custom spinner HTML (use template instead) |
// Deprecated - Error Handling
this.pdfViewer.setErrorHtml("<div>Error</div>");
// New way - Error Handling
// Use [customErrorTpl] with ng-template
// Deprecated - Spinner Handling
this.pdfViewer.setSpinnerHtml("<div>Loading...</div>");
// New way - Spinner Handling
// Use [customSpinnerTpl] with ng-template
We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/intbot/ng2-pdfjs-viewer.git
# Clear Angular cache (Windows)
Remove-Item -Recurse -Force "SampleApp\.angular"
# Build and test - All at once (Windows)
@test.bat
This project is licensed under the Apache License 2.0 + Commons Clause License Condition v1.0 - see the LICENSE file for details.
I take security of this library seriously. If you discover a security vulnerability, please report it privately:
For security best practices and vulnerability reporting guidelines, see our Security Policy.