EmbedPDF
Angular PDF Viewer

An Angular PDF viewer for apps that run on documents

Claims, contracts, onboarding forms, reports. EmbedPDF gives your Angular app one standalone component that reads, fills in, signs and redacts PDFs in the browser. Need your own UI? Every feature is also a service with signals.

4K+stars1M+downloadsApache-2.0 licensed
The EmbedPDF viewer running in a Angular app
app.ts
import { Component } from '@angular/core';
import { EpdfPdfViewer } from '@embedpdf/viewer-angular';

@Component({
  selector: 'app-root',
  imports: [EpdfPdfViewer],
  template: `
    <epdf-pdf-viewer
      src="/report.pdf"
      style="height: 100vh"
    />
  `,
})
export class App {}
Modern Angular
  • Angular 20.2 and later
  • Standalone and OnPush
  • Zoneless ready
Quick start

Up and running in three steps

No module to register, no assets to copy into angular.json. Import the component and use it.

  1. 1

    Install the package

    @embedpdf/viewer-angular holds the viewer, the toolbar and the PDF engine. The engine’s WebAssembly is served from your own app.

    Terminal
    npm install @embedpdf/viewer-angular@next
  2. 2

    Import it and listen with (ready)

    Add EpdfPdfViewer to your component’s imports and put <epdf-pdf-viewer> in the template.

    The (ready) output gives you the viewer. Every feature is a plugin you reach by name.

    app.ts
    import { Component } from '@angular/core';
    import { EpdfPdfViewer, type Viewer } from '@embedpdf/viewer-angular';
    
    @Component({
      selector: 'app-root',
      imports: [EpdfPdfViewer],
      template: `<epdf-pdf-viewer src="/report.pdf" style="height: 100vh" (ready)="onReady($event)" />`,
    })
    export class App {
      onReady(viewer: Viewer) {
        viewer.documents.onOpened(() => viewer.get('search').search({ text: 'total' }));
      }
    }
  3. 3

    Add your own component to the toolbar

    Put any of your components in an ng-template with epdfToolbarItem, and tell the layout where it goes.

    It keeps your services, your styles and your dependency injection.

    app.ts
    import { Component } from '@angular/core';
    import { EpdfPdfViewer, EpdfToolbarItem, type Layout } from '@embedpdf/viewer-angular';
    import { DocumentStatus } from './document-status';
    
    @Component({
      selector: 'app-root',
      imports: [EpdfPdfViewer, EpdfToolbarItem, DocumentStatus],
      template: `
        <epdf-pdf-viewer src="/report.pdf" [layout]="layout" style="height: 100vh">
          <ng-template epdfToolbarItem="status">
            <app-document-status />
          </ng-template>
        </epdf-pdf-viewer>
      `,
    })
    export class App {
      readonly layout = (layout: Layout) => layout.add({ custom: 'status' }, { to: 'main', section: 'end' });
    }
Signals and services

Your own UI, with the Angular you know

Install @embedpdf/angular when the ready-made toolbar is not enough. Each feature is one service: inject(EpdfSearch) for search, inject(EpdfAnnotation) for annotations, and so on.

Call its methods from your event bindings. Read its state as signals in your template. A signal only updates what reads it, so a hit counter never redraws a list.

  • State as signals, events as RxJS streams
  • PDF form fields as Reactive Forms controls, with form.controlOf()
  • Works with @if, @for and @defer
  • Easy to test: provide a fake service like any other
search.ts
import { Component, inject } from '@angular/core';
import { EpdfRenderLayer } from '@embedpdf/angular/render';
import { EpdfSearch, EpdfSearchLayer } from '@embedpdf/angular/search';
import { EpdfPageTemplate, EpdfStage } from '@embedpdf/angular/stage';

@Component({
  selector: 'app-search',
  imports: [EpdfStage, EpdfPageTemplate, EpdfRenderLayer, EpdfSearchLayer],
  template: `
    <form (submit)="$event.preventDefault()">
      <input #query (input)="search.search({ text: query.value })" />
      @if (search.status() === 'searching') {
        <span>Searching…</span>
      }
      @if (search.hitCount() > 0) {
        <span>{{ search.activeHitIndex() + 1 }} of {{ search.hitCount() }}</span>
      }
      <button type="button" (click)="search.previousHit()">↑</button>
      <button type="button" (click)="search.nextHit()">↓</button>
    </form>

    <epdf-stage>
      <ng-template epdfPage>
        <epdf-render-layer />
        <epdf-search-layer />
      </ng-template>
    </epdf-stage>
  `,
})
export class SearchPage {
  protected readonly search = inject(EpdfSearch);
}
Security review

Answers for your security team

The questions that usually stall a PDF library in review, answered up front.

Files stay in the browser

The PDF engine runs as WebAssembly in a Web Worker. Documents are never uploaded to us or anyone else.

No third-party calls

Nothing is fetched from an outside server unless you set it up, and our nightly tests fail on any request that leaves your app. It works in air-gapped networks.

Strict CSP

It runs under a strict Content Security Policy with no unsafe-inline. The docs list the exact policy to copy.

Permissions that hold

Give a document a scope, and the engine checks it on every call, from a button or from code.

Redaction that removes

Redacted text and images are taken out of the file, so they can’t be copied or searched later.

A license legal approves

Apache-2.0: free for commercial use, no copyleft, and an explicit patent grant.
Angular SSR

Server-rendered, and loaded when needed

The package is safe to import on the server, so Angular SSR and prerendering just work. The server sends an empty box of the right size, and the viewer starts in the browser.

Wrap it in @defer (on viewport) and the viewer’s code is not even part of the first download. It loads when the user scrolls to it.

  • No isPlatformBrowser checks around the viewer
  • A placeholder keeps the layout from jumping
  • Faster first load on pages where the PDF is further down
report.ts
import { Component } from '@angular/core';
import { EpdfPdfViewer } from '@embedpdf/viewer-angular';

@Component({
  selector: 'app-report',
  imports: [EpdfPdfViewer],
  template: `
    @defer (on viewport) {
      <epdf-pdf-viewer src="/report.pdf" style="height: 100vh" />
    } @placeholder {
      <div style="height: 100vh"></div>
    }
  `,
})
export class Report {}
FAQ

Angular PDF viewer FAQ

What Angular teams ask before they adopt it.

Which Angular versions are supported?

Angular 20.2 and later. Everything is a standalone component, uses OnPush change detection and is built on signals, so it fits apps made with the current Angular defaults.

Can I use it in an app that still uses NgModules?

Yes. EpdfPdfViewer is a standalone component, and Angular lets you add standalone components to the imports of an NgModule. You don’t have to migrate your app first.

Does it need zone.js?

No. The components are OnPush and update through signals, so they work in zoneless apps that use provideZonelessChangeDetection(), the default for new Angular apps. They work in apps with zone.js too.

Does it work with Angular SSR?

Yes. The package is safe to import on the server, which renders an empty box of the size you set. Wrap the viewer in @defer (on viewport) and its code also stays out of the first download until the user scrolls to it.

How is it different from ngx-extended-pdf-viewer?

ngx-extended-pdf-viewer brings Mozilla’s PDF.js viewer, the one in Firefox, into Angular and lets you configure it. EmbedPDF gives you an Angular API of its own: a standalone component with inputs and outputs, and headless services with signals if you build your own UI. It runs on PDFium, the engine inside Chrome. It also includes real redaction and digital signatures with a key you control.

We embedded the version 2 snippet in Angular. What should we use now?

Use @embedpdf/viewer-angular. Version 2 had no Angular package, so many teams called @embedpdf/snippet from a component by hand. Version 3 gives you a real standalone component with inputs and outputs, and the migration guide in the docs maps each old config key to its new name.

Are documents sent to a server?

No. The PDF engine runs as WebAssembly in a Web Worker inside the browser. Documents are opened, changed and saved on the user’s device, and nothing is fetched from a third party at runtime.

What license is it under?

Apache-2.0. It is free for commercial use, it does not require you to open source your own code, and it includes an explicit patent grant, which legal teams tend to like.

Ready when you are

Put PDFs to work in your Angular app

One standalone component today. Signals and services when you need more. Open source, Apache-2.0, and no documents leaving the browser.

Terminal
npm install @embedpdf/viewer-angular@next