ng2-pdf-viewer vs ng2-pdfjs-viewer vs ngx-extended-pdf-viewer vs pdfjs-dist vs react-pdf vs vue-pdf-embed
PDF Rendering Libraries for Web Applications
ng2-pdf-viewerng2-pdfjs-viewerngx-extended-pdf-viewerpdfjs-distreact-pdfvue-pdf-embedSimilar Packages:

PDF Rendering Libraries for Web Applications

ng2-pdf-viewer, ng2-pdfjs-viewer, ngx-extended-pdf-viewer, pdfjs-dist, react-pdf, and vue-pdf-embed are npm packages that enable PDF rendering in web applications. They all leverage Mozilla's PDF.js library under the hood but differ significantly in their level of abstraction, framework integration, feature completeness, and maintenance status. These libraries range from low-level access (pdfjs-dist) to high-level, framework-specific components that provide ready-to-use UI elements for viewing, navigating, and interacting with PDF documents.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
ng2-pdf-viewer0-282 kB-a year agoMIT
ng2-pdfjs-viewer024411.1 MB196 months agoApache-2.0 + Commons Clause
ngx-extended-pdf-viewer057454.2 MB1419 days agoApache-2.0
pdfjs-dist053,31935.3 MB44818 days agoApache-2.0
react-pdf011,064309 kB193 months agoMIT
vue-pdf-embed01,0235.26 MB92 months agoMIT

PDF Rendering in Modern Web Apps: A Deep Dive into Framework-Specific and Low-Level Solutions

Displaying PDFs in web applications is a common but surprisingly nuanced task. The six packages under review — ng2-pdf-viewer, ng2-pdfjs-viewer, ngx-extended-pdf-viewer, pdfjs-dist, react-pdf, and vue-pdf-embed — represent different approaches: from thin wrappers around Mozilla’s PDF.js to full-featured, framework-native components. Let’s break down how they work, where they excel, and what trade-offs you’ll face.

🧱 Core Architecture: Wrapper vs Native Integration

All these libraries ultimately rely on Mozilla’s PDF.js for rendering, but they differ in how tightly they integrate it with their target frameworks.

pdfjs-dist is the raw, official PDF.js distribution. It gives you full control but requires manual DOM management and event wiring.

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

pdfjsLib.GlobalWorkerOptions.workerSrc = './pdf.worker.min.js';

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

ng2-pdf-viewer, ng2-pdfjs-viewer, and ngx-extended-pdf-viewer are Angular wrappers. They encapsulate this boilerplate into components, but with varying degrees of feature completeness.

<!-- ng2-pdf-viewer: Minimal Angular component -->
<pdf-viewer [src]="'document.pdf'" [page]="1" [original-size]="true"></pdf-viewer>
<!-- ngx-extended-pdf-viewer: Feature-rich Angular component -->
<ngx-extended-pdf-viewer
  [src]="'document.pdf'"
  [showHandTool]="true"
  [showFindButton]="true"
  [filenameForDownload]="'custom-name.pdf'">
</ngx-extended-pdf-viewer>

react-pdf provides React hooks and components that abstract PDF.js while embracing React’s declarative model.

// react-pdf: React component with hooks
import { Document, Page } from 'react-pdf';

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

vue-pdf-embed follows a similar pattern for Vue, offering a single component that handles rendering.

<!-- vue-pdf-embed: Vue component -->
<template>
  <vue-pdf-embed :src="'document.pdf'" page="1" />
</template>

<script>
import { VuePdfEmbed } from 'vue-pdf-embed';
export default { components: { VuePdfEmbed } };
</script>

🔍 Feature Completeness: Basic Rendering vs Full Viewer UI

If you only need to display a static page, most libraries will suffice. But real-world apps often require text selection, zoom controls, search, printing, or form filling.

ngx-extended-pdf-viewer stands out by bundling a near-complete replica of the official PDF.js viewer UI, including toolbar, sidebar, and annotation support. It even supports printing and downloading with custom filenames.

// ngx-extended-pdf-viewer: Full UI with customization
<ngx-extended-pdf-viewer
  [src]="pdfSrc"
  [useBrowserLocale]="true"
  [showBorders]="false"
  [showPresentationModeButton]="true">
</ngx-extended-pdf-viewer>

react-pdf focuses on core rendering and leaves UI controls to the developer. You’ll need to build your own toolbar for zoom, page navigation, etc.

// react-pdf: Manual page navigation
import { useState } from 'react';
import { Document, Page, pdfjs } from 'react-pdf';

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

function PDFViewer() {
  const [pageNumber, setPageNumber] = useState(1);
  return (
    <div>
      <button onClick={() => setPageNumber(p => p - 1)} disabled={pageNumber <= 1}>Prev</button>
      <Document file="document.pdf">
        <Page pageNumber={pageNumber} />
      </Document>
      <button onClick={() => setPageNumber(p => p + 1)}>Next</button>
    </div>
  );
}

ng2-pdf-viewer and ng2-pdfjs-viewer offer basic rendering but lack advanced features like text layer support or form interaction. ng2-pdfjs-viewer appears to be less actively maintained and has fewer configuration options.

vue-pdf-embed provides solid rendering but minimal UI — similar to react-pdf in scope.

<!-- vue-pdf-embed: Basic usage only -->
<vue-pdf-embed :src="pdfUrl" :page="currentPage" @loaded="onLoaded" />

⚙️ Customization and Control

When you need fine-grained control over rendering (e.g., custom overlays, dynamic annotations, or integration with drawing tools), lower-level access matters.

pdfjs-dist gives you direct access to every PDF.js API — page objects, text layers, annotation layers, and more. This is essential for complex use cases like collaborative document editing.

// pdfjs-dist: Access text content
const page = await pdf.getPage(1);
const textContent = await page.getTextContent();
console.log(textContent.items); // Array of text items with positions

react-pdf exposes some internals via props like customTextRenderer and onGetAnnotations, but it’s still an abstraction layer.

// react-pdf: Custom text rendering
<Page
  pageNumber={1}
  customTextRenderer={({ str }) => <mark>{str}</mark>}
/>

ngx-extended-pdf-viewer provides extensive Angular-specific inputs and outputs to customize behavior without dropping down to PDF.js APIs directly.

// ngx-extended-pdf-viewer: Event handling
<ngx-extended-pdf-viewer
  (pageChange)="onPageChange($event)"
  (textLayerRendered)="onTextRendered($event)">
</ngx-extended-pdf-viewer>

The other Angular and Vue packages offer limited hooks for customization, often requiring DOM manipulation or workarounds.

📦 Bundle Size and Performance Considerations

PDF.js is large (~1MB minified). All these packages inherit that weight, but they differ in how they handle code splitting and worker loading.

pdfjs-dist requires you to manually configure the worker path, which is critical for performance.

// Must set workerSrc before using PDF.js
pdfjsLib.GlobalWorkerOptions.workerSrc = '/assets/pdf.worker.min.js';

react-pdf and ngx-extended-pdf-viewer provide utilities to simplify worker loading, often supporting CDN or dynamic imports.

// react-pdf: Worker via CDN
import { pdfjs } from 'react-pdf';
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`;

ng2-pdf-viewer and vue-pdf-embed typically bundle the worker or require manual setup, which can lead to larger initial bundles if not handled carefully.

🛑 Maintenance Status and Deprecation Warnings

As of the latest package metadata:

  • ng2-pdfjs-viewer shows signs of reduced maintenance. Its documentation is sparse, and recent issues suggest compatibility problems with newer Angular versions. Avoid for new projects.
  • ng2-pdf-viewer is still maintained but has a narrower feature set compared to ngx-extended-pdf-viewer.
  • ngx-extended-pdf-viewer, react-pdf, and vue-pdf-embed are actively updated and support modern framework versions.
  • pdfjs-dist is the canonical PDF.js package, maintained by Mozilla, and should always be used when direct access is needed.

🔄 Real-World Migration Scenarios

Scenario 1: Adding PDF Viewing to an Existing Angular App

  • If you need a quick, basic viewer: ng2-pdf-viewer
  • If you need full viewer functionality (search, print, forms): ngx-extended-pdf-viewer
  • Avoid ng2-pdfjs-viewer due to maintenance concerns.

Scenario 2: Building a React-Based Document Editor

  • Use react-pdf for rendering within React’s lifecycle.
  • Drop down to pdfjs-dist only if you need low-level text/annotation manipulation.

Scenario 3: Lightweight Vue App with Occasional PDF Display

  • vue-pdf-embed is sufficient for simple use cases.
  • For complex interactions, consider integrating pdfjs-dist directly.

📊 Summary Table

PackageFrameworkFeature CompletenessCustomizationMaintenance Status
ng2-pdf-viewerAngularBasicLowActive
ng2-pdfjs-viewerAngularBasicLowNot recommended
ngx-extended-pdf-viewerAngularFull viewer UIHighActive
pdfjs-distNone (vanilla)Full API accessHighestActive (Mozilla)
react-pdfReactCore renderingMediumActive
vue-pdf-embedVueBasicLowActive

💡 Final Recommendation

  • Need a drop-in, full-featured PDF viewer in Angular?ngx-extended-pdf-viewer
  • Building a React app and want idiomatic integration?react-pdf
  • Working with Vue and need simplicity?vue-pdf-embed
  • Require maximum control or are building a framework-agnostic solution?pdfjs-dist
  • Avoid ng2-pdfjs-viewer for new projects due to unclear maintenance and limited features.

Choose based on your framework, required features, and how much control you need over the underlying PDF.js engine.

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

  • ng2-pdf-viewer:

    Choose ng2-pdf-viewer if you're working in an Angular application and need a lightweight, straightforward PDF viewer for basic rendering without advanced features like text selection, search, or form filling. It's suitable for simple use cases where you control the PDF content and don't require a full toolbar or sidebar functionality.

  • ng2-pdfjs-viewer:

    Avoid ng2-pdfjs-viewer for new projects. The package shows signs of reduced maintenance, has sparse documentation, and lacks the feature set and active support found in alternatives like ngx-extended-pdf-viewer. If you encounter it in legacy code, plan a migration to a more robust solution.

  • ngx-extended-pdf-viewer:

    Choose ngx-extended-pdf-viewer if you're building an Angular application that requires a full-featured PDF viewer with built-in toolbar, sidebar, search, printing, form support, and extensive customization options. It provides the most complete PDF.js experience in the Angular ecosystem with active maintenance and rich configuration.

  • pdfjs-dist:

    Choose pdfjs-dist when you need direct, low-level access to Mozilla's PDF.js library for maximum control over rendering, text extraction, annotation handling, or custom integrations. This is ideal for framework-agnostic solutions, complex document editing scenarios, or when you must build a highly customized viewer from scratch.

  • react-pdf:

    Choose react-pdf if you're developing a React application and want a well-maintained, idiomatic way to render PDFs using React components and hooks. It handles core rendering efficiently but requires you to build additional UI controls like navigation or zoom yourself, making it best for projects where you need React integration without a full pre-built toolbar.

  • vue-pdf-embed:

    Choose vue-pdf-embed if you're working with Vue and need a simple, component-based approach to embed PDF pages with minimal setup. It's appropriate for basic viewing needs in Vue applications but lacks advanced features and deep customization, so consider pdfjs-dist directly if you require more control.

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