ng2-pdf-viewer vs ng2-pdfjs-viewer vs ngx-extended-pdf-viewer vs pdfjs-dist vs react-pdf
Rendering PDFs in Angular and React Applications
ng2-pdf-viewerng2-pdfjs-viewerngx-extended-pdf-viewerpdfjs-distreact-pdfSimilar Packages:

Rendering PDFs in Angular and React Applications

This comparison evaluates five key libraries for displaying PDF documents in modern web applications. pdfjs-dist is the core rendering engine maintained by Mozilla, serving as the foundation for the other wrappers. react-pdf is the standard React component wrapper around this engine. For Angular developers, ng2-pdf-viewer, ng2-pdfjs-viewer, and ngx-extended-pdf-viewer offer varying levels of abstraction and feature completeness, with ngx-extended-pdf-viewer currently standing as the most robust and maintained Angular-specific solution.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
ng2-pdf-viewer0-282 kB-2 years agoMIT
ng2-pdfjs-viewer024411.1 MB207 months agoApache-2.0 + Commons Clause
ngx-extended-pdf-viewer057954.2 MB112 months agoApache-2.0
pdfjs-dist053,43235.6 MB42912 days agoApache-2.0
react-pdf011,091309 kB194 months agoMIT

Rendering PDFs in Angular and React Applications

Displaying PDF files in a web browser is a common requirement for dashboards, document management systems, and reporting tools. The ecosystem offers several options ranging from low-level rendering engines to high-level UI components. This analysis compares pdfjs-dist, react-pdf, and three Angular-specific wrappers (ng2-pdf-viewer, ng2-pdfjs-viewer, ngx-extended-pdf-viewer) to help you make the right architectural choice.

🏗️ Core Architecture: Engine vs. Wrapper

Understanding the relationship between these packages is critical. pdfjs-dist is the core engine. The others are wrappers that make it easier to use within specific frameworks.

pdfjs-dist is the official Mozilla library. It handles the heavy lifting of parsing and rendering. You manage the DOM and state yourself.

// pdfjs-dist: Manual rendering setup
import * as pdfjsLib from 'pdfjs-dist';

const loadingTask = pdfjsLib.getDocument('file.pdf');
loadingTask.promise.then(function(pdf) {
  pdf.getPage(1).then(function(page) {
    const scale = 1.5;
    const viewport = page.getViewport({ scale: scale });
    const canvas = document.getElementById('the-canvas');
    const context = canvas.getContext('2d');
    canvas.height = viewport.height;
    canvas.width = viewport.width;
    page.render({ canvasContext: context, viewport: viewport });
  });
});

react-pdf wraps this engine in React components. It handles the lifecycle for you.

// react-pdf: Component-based usage
import { Document, Page } from 'react-pdf';

function MyApp() {
  return (
    <Document file="file.pdf">
      <Page pageNumber={1} />
    </Document>
  );
}

ngx-extended-pdf-viewer wraps the engine for Angular with a full UI toolbar.

<!-- ngx-extended-pdf-viewer: Full UI component -->
<ngx-extended-pdf-viewer
  [src]="'file.pdf'"
  [useBrowserLocale]="true"
  height="90vh">
</ngx-extended-pdf-viewer>

ng2-pdf-viewer is an older Angular wrapper that renders pages as canvas elements without the full toolbar UI by default.

<!-- ng2-pdf-viewer: Basic canvas rendering -->
<pdf-viewer
  [src]="pdfSrc"
  [render-text]="true"
  [show-all]="true"
  style="display: block;">
</pdf-viewer>

ng2-pdfjs-viewer is another Angular wrapper that aims to expose more of the underlying PDF.js viewer functionality than the basic canvas renderer.

<!-- ng2-pdfjs-viewer: Exposing viewer options -->
<pdfjs-viewer
  [src]="pdfSrc"
  [show-toolbar]="true"
  style="width: 100%; height: 100%;">
</pdfjs-viewer>

🎨 User Interface: Built-in vs. Custom

The biggest difference lies in how much UI work you want to do yourself.

ngx-extended-pdf-viewer provides a complete interface. It includes zoom controls, page navigation, search, and printing buttons out of the box. This saves weeks of development time if you need a standard reader experience.

<!-- ngx-extended-pdf-viewer: Built-in search and print -->
<ngx-extended-pdf-viewer
  [src]="documentUrl"
  [show-search-button]="true"
  [show-print-button]="true">
</ngx-extended-pdf-viewer>

react-pdf gives you the rendering but not the controls. You must build your own toolbar for zooming or page turning. This offers maximum design flexibility but requires more code.

// react-pdf: Custom controls required
function Reader({ file }) {
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);

  function onDocumentLoadSuccess({ numPages }) {
    setNumPages(numPages);
  }

  return (
    <div>
      <button onClick={() => setPageNumber(prev => prev - 1)}>Prev</button>
      <Document file={file} onLoadSuccess={onDocumentLoadSuccess}>
        <Page pageNumber={pageNumber} />
      </Document>
      <button onClick={() => setPageNumber(prev => prev + 1)}>Next</button>
    </div>
  );
}

pdfjs-dist requires you to build everything. There is no UI. You are responsible for the canvas, the buttons, and the state management.

// pdfjs-dist: Completely custom UI implementation
const canvas = document.createElement('canvas');
// ... rendering logic ...
document.getElementById('controls').appendChild(canvas);
// You must write event listeners for zoom/pan manually

ng2-pdf-viewer focuses on rendering pages in the flow. It does not include the Adobe-style toolbar by default. You build the navigation logic in your Angular component.

<!-- ng2-pdf-viewer: Custom navigation logic -->
<pdf-viewer
  [src]="src"
  [(page)]="page"
  [zoom]="zoom">
</pdf-viewer>
<button (click)="page = page - 1">Previous</button>
<button (click)="page = page + 1">Next</button>

ng2-pdfjs-viewer attempts to bridge the gap by including some viewer options, but it often requires more configuration to match the polish of ngx-extended-pdf-viewer.

<!-- ng2-pdfjs-viewer: Configuring viewer options -->
<pdfjs-viewer
  [src]="src"
  [options]="{ showToolbar: true }">
</pdfjs-viewer>

⚙️ Configuration and Setup

Setting up the worker file is a common pain point with PDF.js based libraries. The worker is a separate script that handles parsing to keep the UI thread free.

react-pdf requires you to set the worker source explicitly to match the version you are using. Mismatched versions cause runtime errors.

// react-pdf: Setting the worker source
import { pdfjs } from 'react-pdf';

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.js`;

pdfjs-dist requires similar manual configuration of the GlobalWorkerOptions.

// pdfjs-dist: Manual worker configuration
pdfjsLib.GlobalWorkerOptions.workerSrc = 'path/to/pdf.worker.js';

ngx-extended-pdf-viewer simplifies this for Angular. It often handles the worker path automatically or provides clear Angular CLI configuration steps to copy the assets.

// ngx-extended-pdf-viewer: Angular module config
import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer';

@NgModule({
  imports: [
    NgxExtendedPdfViewerModule.useDefaults()
  ]
})
export class AppModule {}

ng2-pdf-viewer requires you to configure the worker source in your root component or module setup, similar to the raw library.

// ng2-pdf-viewer: Worker setup in component
import { PdfViewerModule } from 'ng2-pdf-viewer';
import * as pdfjsLib from 'pdfjs-dist';

pdfjsLib.GlobalWorkerOptions.workerSrc = 'path/to/pdf.worker.js';

ng2-pdfjs-viewer also relies on the underlying pdfjs-dist worker configuration, meaning you must ensure the paths are correct in your build process.

// ng2-pdfjs-viewer: Ensuring worker path is valid
// Typically configured in angular.json assets or component init

📱 Responsiveness and Performance

Performance depends on how the library handles large documents and viewport changes.

react-pdf renders pages as canvas elements. It supports lazy loading pages, which is crucial for large files. You control when pages render.

// react-pdf: Lazy loading pages
<Document file={largeFile}>
  <Page pageNumber={1} renderAnnotationLayer={false} />
  <Page pageNumber={2} renderAnnotationLayer={false} />
</Document>

ngx-extended-pdf-viewer uses the standard PDF.js viewer which includes virtualization (only rendering visible pages). This handles large files well without extra code from you.

<!-- ngx-extended-pdf-viewer: Automatic virtualization -->
<ngx-extended-pdf-viewer
  [src]="largeFile"
  [show-all]="false">
</ngx-extended-pdf-viewer>

pdfjs-dist gives you raw control. You must implement virtualization logic yourself if you want to support 100+ page documents without freezing the browser.

// pdfjs-dist: Manual virtualization logic required
// Listen to scroll events and render only visible pages
container.addEventListener('scroll', () => {
  // Calculate visible range and render pages
});

ng2-pdf-viewer renders all pages by default if show-all is true, which can cause performance issues with large files. You should paginate manually for better performance.

<!-- ng2-pdf-viewer: Pagination for performance -->
<pdf-viewer
  [src]="src"
  [show-all]="false"
  [page]="currentPage">
</pdf-viewer>

ng2-pdfjs-viewer inherits the performance characteristics of the PDF.js viewer it wraps, generally handling virtualization better than basic canvas wrappers.

<!-- ng2-pdfjs-viewer: Viewer mode performance -->
<pdfjs-viewer
  [src]="src"
  [view]="'fit-width'">
</pdfjs-viewer>

⚠️ Maintenance and Deprecation Status

This is a critical factor for long-term projects.

ng2-pdf-viewer is effectively in maintenance mode or deprecated for new features. The community has largely moved to ngx-extended-pdf-viewer. Using this for a new project introduces technical debt.

// ng2-pdf-viewer: Legacy implementation
// Avoid for new projects
import { PdfViewerModule } from 'ng2-pdf-viewer';

ngx-extended-pdf-viewer is actively maintained. It tracks updates to the underlying PDF.js library and fixes Angular-specific integration issues regularly.

// ngx-extended-pdf-viewer: Active maintenance
import { NgxExtendedPdfViewerModule } from 'ngx-extended-pdf-viewer';

react-pdf is the standard for React. It is actively maintained and updated to support recent React versions and PDF.js updates.

// react-pdf: Standard React implementation
import { Document } from 'react-pdf';

pdfjs-dist is maintained by Mozilla. It is stable but changes occasionally in breaking ways, which is why wrappers exist.

// pdfjs-dist: Core engine
import * as pdfjsLib from 'pdfjs-dist';

ng2-pdfjs-viewer has lower adoption and update frequency compared to ngx-extended-pdf-viewer. Verify the latest commit history before relying on it for critical features.

<!-- ng2-pdfjs-viewer: Check maintenance status -->
<pdfjs-viewer [src]="src"></pdfjs-viewer>

📊 Summary: Key Differences

Featurepdfjs-distreact-pdfngx-extended-pdf-viewerng2-pdf-viewer
FrameworkVanilla JSReactAngularAngular
UI ComponentsNoneCanvas OnlyFull ToolbarCanvas Only
Setup ComplexityHighMediumLowMedium
MaintenanceActive (Mozilla)ActiveActiveLow / Legacy
Best ForCustom EnginesReact AppsAngular Enterprise AppsLegacy Angular Apps

💡 Final Recommendation

For React Developers: Use react-pdf. It is the community standard. You will find the most support and examples online. Be prepared to build your own toolbar.

For Angular Developers: Use ngx-extended-pdf-viewer. It saves significant development time by providing a complete UI. Avoid ng2-pdf-viewer for new work as it is older technology.

For Custom Needs: Use pdfjs-dist only if you are building your own framework wrapper or have very specific rendering requirements that the existing wrappers cannot satisfy.

How to Choose: ng2-pdf-viewer vs ng2-pdfjs-viewer vs ngx-extended-pdf-viewer vs pdfjs-dist vs react-pdf

  • ng2-pdf-viewer:

    Choose ng2-pdf-viewer only if you are maintaining a legacy Angular application that already depends on it. It is largely considered deprecated in favor of newer wrappers. Do not start new projects with this package as it lacks recent updates and modern feature support compared to alternatives.

  • ng2-pdfjs-viewer:

    Choose ng2-pdfjs-viewer if you need a lightweight Angular wrapper that stays very close to the raw pdfjs-dist API without extra UI features. It is suitable for simple use cases where you want to build custom controls from scratch, but verify its maintenance status before committing as it sees less activity than extended versions.

  • ngx-extended-pdf-viewer:

    Choose ngx-extended-pdf-viewer for new Angular projects requiring a full-featured PDF viewer. It includes a toolbar, search, and printing capabilities out of the box. This is the recommended choice for Angular teams who want a 'batteries-included' experience similar to Adobe Acrobat Reader within their app.

  • pdfjs-dist:

    Choose pdfjs-dist if you are building a custom framework wrapper or need complete control over the rendering pipeline without any UI opinions. It is the underlying engine for the other libraries. Use this if you are comfortable managing the viewer lifecycle, rendering contexts, and event listeners manually in vanilla JavaScript or a custom framework setup.

  • react-pdf:

    Choose react-pdf if you are building an application with React. It provides a clean component-based API to render PDF pages as canvas elements. It is the standard choice for React developers who need to display PDF content while managing state and props within the React ecosystem.

README for ng2-pdf-viewer

Angular PDF Viewer

downloads npm version Gitter PayPal donate button

PDF Viewer Component for Angular 5+

Demo page

https://vadimdez.github.io/ng2-pdf-viewer/

Stackblitz Example

https://stackblitz.com/edit/ng2-pdf-viewer

Blog post

https://medium.com/@vadimdez/render-pdf-in-angular-4-927e31da9c76

Overview

Install

Angular >= 12

npm install ng2-pdf-viewer

Partial Ivy compilated library bundles.

Angular >= 4

npm install ng2-pdf-viewer@^7.0.0

Angular < 4

npm install ng2-pdf-viewer@~3.0.8

Usage

In case you're using systemjs see configuration here.

Add PdfViewerModule to your module's imports

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';

import { PdfViewerModule } from 'ng2-pdf-viewer';

@NgModule({
  imports: [BrowserModule, PdfViewerModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})

class AppModule {}

platformBrowserDynamic().bootstrapModule(AppModule);

And then use it in your component

import { Component } from '@angular/core';

@Component({
  selector: 'example-app',
  template: `
  <pdf-viewer [src]="pdfSrc"
              [render-text]="true"
              [original-size]="false"
              style="width: 400px; height: 500px"
  ></pdf-viewer>
  `
})
export class AppComponent {
  pdfSrc = "https://vadimdez.github.io/ng2-pdf-viewer/assets/pdf-test.pdf";
}

Options

[src]

PropertyTypeRequired
[src]string, object, UInt8ArrayRequired

Pass pdf location

[src]="'https://vadimdez.github.io/ng2-pdf-viewer/assets/pdf-test.pdf'"

For more control you can pass options object to [src]. See other attributes for the object here.

Options object for loading protected PDF would be:

{
 url: 'https://vadimdez.github.io/ng2-pdf-viewer/assets/pdf-test.pdf',
 withCredentials: true
}

[page]

PropertyTypeRequired
[page] or [(page)]numberRequired with [show-all]="false" or Optional with [show-all]="true"

Page number

[page]="1"

supports two way data binding as well

[(page)]="pageVariable"

If you want that the two way data binding actually updates your page variable on page change/scroll - you have to be sure that you define the height of the container, for example:

pdf-viewer {
    height: 100vh;
}

[stick-to-page]

PropertyTypeRequired
[stick-to-page]booleanOptional

Sticks view to the page. Works in combination with [show-all]="true" and page.

[stick-to-page]="true"

[render-text]

PropertyTypeRequired
[render-text]booleanOptional

Enable text rendering, allows to select text

[render-text]="true"

[render-text-mode]

PropertyTypeRequired
[render-text-mode]RenderTextModeOptional

Used in combination with [render-text]="true"

Controls if the text layer is enabled, and the selection mode that is used.

0 = RenderTextMode.DISABLED - disable the text selection layer

1 = RenderTextMode.ENABLED - enables the text selection layer

2 = RenderTextMode.ENHANCED - enables enhanced text selection

[render-text-mode]="1"

[external-link-target]

PropertyTypeRequired
[external-link-target]stringOptional

Used in combination with [render-text]="true"

Link target

  • blank
  • none
  • self
  • parent
  • top
[external-link-target]="'blank'"

[rotation]

PropertyTypeRequired
[rotation]numberOptional

Rotate PDF

Allowed step is 90 degree, ex. 0, 90, 180

[rotation]="90"

[zoom]

PropertyTypeRequired
[zoom]numberOptional

Zoom pdf

[zoom]="0.5"

[zoom-scale]

PropertyTypeRequired
[zoom-scale]'page-width'|'page-fit'|'page-height'Optional

Defines how the Zoom scale is computed when [original-size]="false", by default set to 'page-width'.

  • 'page-width' with zoom of 1 will display a page width that take all the possible horizontal space in the container

  • 'page-height' with zoom of 1 will display a page height that take all the possible vertical space in the container

  • 'page-fit' with zoom of 1 will display a page that will be scaled to either width or height to fit completely in the container

[zoom-scale]="'page-width'"

[original-size]

PropertyTypeRequired
[original-size]booleanOptional
  • if set to true - size will be as same as original document
  • if set to false - size will be as same as container block
[original-size]="true"

[fit-to-page]

PropertyTypeRequired
[fit-to-page]booleanOptional

Works in combination with [original-size]="true". You can show your document in original size, and make sure that it's not bigger then container block.

[fit-to-page]="false"

[show-all]

PropertyTypeRequired
[show-all]booleanOptional

Show single or all pages altogether

[show-all]="true"

[autoresize]

PropertyTypeRequired
[autoresize]booleanOptional

Turn on or off auto resize.

!Important To make [autoresize] work - make sure that [original-size]="false" and pdf-viewer tag has max-width or display are set.

[autoresize]="true"

[c-maps-url]

PropertyTypeRequired
[c-maps-url]stringOptional

Url for non-latin characters source maps.

[c-maps-url]="'assets/cmaps/'"

Default url is: https://unpkg.com/pdfjs-dist@2.0.550/cmaps/

To serve cmaps on your own you need to copy node_modules/pdfjs-dist/cmaps to assets/cmaps.

[show-borders]

PropertyTypeRequired
[show-borders]booleanOptional

Show page borders

[show-borders]="true"

(after-load-complete)

PropertyTypeRequired
(after-load-complete)callbackOptional

Get PDF information with callback

First define callback function "callBackFn" in your controller,

callBackFn(pdf: PDFDocumentProxy) {
   // do anything with "pdf"
}

And then use it in your template:

(after-load-complete)="callBackFn($event)"

(page-rendered)

PropertyTypeRequired
(page-rendered)callbackOptional

Get event when a page is rendered. Called for every page rendered.

Define callback in your component:

pageRendered(e: CustomEvent) {
  console.log('(page-rendered)', e);
}

And then bind it to <pdf-viewer>:

(page-rendered)="pageRendered($event)"

(pages-initialized)

PropertyTypeRequired
(pages-initialized)callbackOptional

Get event when the pages are initialized.

Define callback in your component:

pageInitialized(e: CustomEvent) {
  console.log('(pages-initialized)', e);
}

And then bind it to <pdf-viewer>:

(pages-initialized)="pageInitialized($event)"

(text-layer-rendered)

PropertyTypeRequired
(text-layer-rendered)callbackOptional

Get event when a text layer is rendered.

Define callback in your component:

textLayerRendered(e: CustomEvent) {
  console.log('(text-layer-rendered)', e);
}

And then bind it to <pdf-viewer>:

(text-layer-rendered)="textLayerRendered($event)"

(error)

PropertyTypeRequired
(error)callbackOptional

Error handling callback

Define callback in your component's class

onError(error: any) {
  // do anything
}

Then add it to pdf-component in component's template

(error)="onError($event)"

(on-progress)

PropertyTypeRequired
(on-progress)callbackOptional

Loading progress callback - provides progress information total and loaded bytes. Is called several times during pdf loading phase.

Define callback in your component's class

onProgress(progressData: PDFProgressData) {
  // do anything with progress data. For example progress indicator
}

Then add it to pdf-component in component's template

(on-progress)="onProgress($event)"

Render local PDF file

In your html template add input:

<input (change)="onFileSelected()" type="file" id="file">

and then add onFileSelected method to your component:

onFileSelected() {
  let $img: any = document.querySelector('#file');

  if (typeof (FileReader) !== 'undefined') {
    let reader = new FileReader();

    reader.onload = (e: any) => {
      this.pdfSrc = e.target.result;
    };

    reader.readAsArrayBuffer($img.files[0]);
  }
}

Set custom path to the worker

By default the worker is loaded from cdn.jsdelivr.net.

In your code update path to the worker to be for example /pdf.worker.mjs

(window as any).pdfWorkerSrc = '/pdf.worker.mjs';

This should be set before pdf-viewer component is rendered.

If you ever have a (super rare) edge case where you run in an environment that multiple components are somehow loaded within the same web page, sharing the same window, but using different versions of pdf.worker, support has been added. You can do the above, except that you can append the specific version of pdfjs required and override the custom path just for that version. This way setting the global window var won't conflict.

(window as any)["pdfWorkerSrc2.14.305"] = '/pdf.worker.mjs';

Search in the PDF

Use eventBus for the search functionality.

In your component's ts file:

  • Add reference to pdf-viewer component,
  • then when needed execute search() like this:
@ViewChild(PdfViewerComponent) private pdfComponent: PdfViewerComponent;

search(stringToSearch: string) {
  this.pdfComponent.eventBus.dispatch('find', {
    query: stringToSearch, type: 'again', caseSensitive: false, findPrevious: undefined, highlightAll: true, phraseSearch: true
  });
}

Contribute

See CONTRIBUTING.md

Donation

If this project help you reduce time to develop, you can give me a cup of tea :)

paypal

License

MIT © Vadym Yatsyuk