CloudPDF
DocsPricing
Start building

Getting Started

The headless libraries give you the engine and the plugin system — you own every pixel of the UI. An app depends on two packages: the adapter for your framework, and an engine.

Installation#

npm install @embedpdf/angular@next @cloudpdf/engine@next

The Angular adapter ships each vertical as a secondary entry point — Angular’s native library modularity — with inject functions instead of hooks.

Your first viewer#

cloudEngine({ baseUrl }) creates the engine — synchronously and cheaply: it holds an HTTP client and nothing else, so a module-scope const engine = cloudEngine({ baseUrl }) is safe (even under SSR). Hand it to <Viewer>; every open, render, and text call rides HTTPS to your CloudPDF deployment. This is the whole app:

Loading live preview…

basic.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { cloudEngine } from '@cloudpdf/engine';
import { EpdfViewer, injectDocumentId } from '@embedpdf/angular/runtime';
import type { EpdfInitialDocument, OpenInput } from '@embedpdf/angular/runtime';
import { EpdfPageTemplate, EpdfStage, stagePlugin } from '@embedpdf/angular/stage';
import { EpdfRenderLayer, renderPlugin } from '@embedpdf/angular/render';

const ebook: OpenInput = { kind: 'share', shareToken: 'shr_WGj1goAtlNN_fQ5OswPrbJQM' };

// Kernel readers live INSIDE <epdf-viewer>, where the host is injectable —
// and document UI is gated on having a document.
@Component({
  selector: 'demo-workspace',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [EpdfStage, EpdfPageTemplate, EpdfRenderLayer],
  template: `
    @if (documentId()) {
      <epdf-stage style="display: block; height: 100%">
        <ng-template epdfPage>
          <epdf-render-layer />
        </ng-template>
      </epdf-stage>
    } @else {
      <p>Loading…</p>
    }
  `,
})
export class Workspace {
  readonly documentId = injectDocumentId();
}

@Component({
  selector: 'demo-root',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [EpdfViewer, Workspace],
  template: `
    <epdf-viewer [engine]="engine" [plugins]="plugins" [initialDocuments]="initialDocuments">
      <div style="height: 500px">
        <demo-workspace />
      </div>
    </epdf-viewer>
  `,
})
export class App {
  // The engine is created synchronously and costs nothing until first use, so
  // a field initializer is safe. The viewer warms it up when the kernel
  // materializes; only opening a document does real work — the UI renders
  // at t≈0.
  readonly engine = cloudEngine({ baseUrl: 'https://engine.cloudpdf.com' });
  readonly plugins = [stagePlugin(), renderPlugin()];
  readonly initialDocuments: EpdfInitialDocument[] = [{ source: ebook }];
}

Prefer the viewer to own the engine’s lifetime — created on mount, destroyed on unmount? Pass a thunk instead: engine={() => cloudEngine({ baseUrl })}. See the Engine getting started for tokens, public shares, and the ownership model.