EmbedPDF

Documents

Give the viewer a document with src: a URL, a File, or bytes.

{ src: '/reports/2026-q3.pdf' }

A relative URL is relative to your page. A URL on another site needs CORS headers that allow yours, like any fetch.

Open the file someone picks#

src follows its prop: give it another document, and the viewer opens that one in its place.

pick-file.ts
import { Component, signal } from '@angular/core';
import { EpdfPdfViewer } from '@embedpdf/viewer-angular';

@Component({
  selector: 'app-upload',
  imports: [EpdfPdfViewer],
  template: `
    <input type="file" accept="application/pdf" (change)="pick($event)" />
    <epdf-pdf-viewer [src]="file()" style="height: 80vh" />
  `,
})
export class Upload {
  readonly file = signal<File | null>(null);

  pick(event: Event) {
    this.file.set((event.target as HTMLInputElement).files?.[0] ?? null);
  }
}

People can also open files themselves: drop a PDF on the viewer, or use Open in the document menu. Both open it as a new tab.

Several documents#

Give documents instead of src to open several at once. Each one gets a tab:

{
  documents: [
    { src: '/contract.pdf', name: 'Contract' },
    { src: '/appendix.pdf', name: 'Appendix A' },
    { src: '/scan.pdf', name: 'Signed scan', active: true },
  ],
}
FieldWhat it isDefault
srcA URL, a File, or bytesnone
nameWhat its tab showsthe file’s name
passwordIts password, if it has onenone
activeShow this one firstthe first document

The tabs show while more than one document is open. To show them always or never, set layout.tabs('always') or layout.tabs('never') in your layout. onDocumentChange tells you which document is showing each time that changes, and null once none is.

Passwords#

A document with a password opens locked, and the viewer asks for it. When you know it, pass it along and nobody is asked:

{ documents: [{ src: '/payroll.pdf', password: secret }] }

A wrong password asks again. A document that can’t be opened at all shows why in its tab, with a Try again button.

Open and close documents from code#

viewer.documents opens, closes and switches documents. It’s the documents plugin, the same as in headless:

const { document } = await viewer.documents.open(
  { kind: 'url', url: '/invoice-1042.pdf' },
  { name: 'Invoice 1042' },
);
 
viewer.documents.setActive(document.id);
viewer.documents.close(document.id);

open() resolves once the document is ready, and makes it the active one.

Download and print#

The document menu has Download and Print, and Ctrl/Cmd+S and Ctrl/Cmd+P work while the viewer has focus. Both include every annotation, form value and signature. When the scope doesn’t allow printing, or you turn it off, Print isn’t there, and Ctrl/Cmd+P says so instead of printing the web page.

To run them from your own button, use the commands document:download and document:print, or call them from code:

viewer.commands.execute('document:print');

Save to your server#

viewer.documents.download() gives you the PDF with every change, as bytes, without a download dialog. Make it a button of your own:

{
  commands: [
    {
      id: 'acme:save',
      label: 'Save',
      icon: 'save',
      shortcut: 'Mod+S',
      run: async ({ viewer, documentId }) => {
        const bytes = await viewer.documents.download(documentId);
        await fetch(`/api/documents/${documentId}`, { method: 'PUT', body: bytes });
      },
    },
  ],
  layout: (layout) => layout.replace('document:download', 'acme:save'),
}

Mod+S now saves to your server instead of downloading, because a command of yours wins over a built-in one with the same shortcut.

Warn before changes are lost#

When someone closes a tab or leaves the page with unsaved changes, the viewer asks first. Unsaved means changed since it opened or since the last download. viewer.documents.get().hasUnsavedChanges tells you the same, and onUnsavedChangesChanged fires when it changes:

viewer.documents.onUnsavedChangesChanged(({ documentId, hasUnsavedChanges }) => {
  saveButton.disabled = !hasUnsavedChanges;
});

Good to know#

Big files#

The local engine needs the whole file before the first page shows, so a large scan takes as long as its download. The toolbar is there at once; only the pages wait.

Where the bytes go#

With the local engine, a document never leaves the browser: no upload, no server. Each document runs in the engine’s worker, so a heavy page doesn’t freeze your app.

Every documents method#

viewer.documents is the headless documents plugin. Its methods, state and events are listed on Opening documents, Several documents and Saving.

Was this page helpful?

Your feedback goes directly to the documentation team.