Setup
A headless viewer needs two packages: the one for your framework, and an engine. This page installs them, creates the engine for your whole app, and covers server-side rendering, bundlers and fonts.
Install#
npm install @embedpdf/svelte@next @embedpdf/engine@nextThe framework package holds every plugin, one import path each (@embedpdf/svelte/search,
@embedpdf/svelte/annotation). A plugin you don’t import isn’t in your bundle.
It’s written for Svelte 5: components that take snippets, and functions that give you reactive state for your runes.
Create the engine once#
Create one engine when your app starts, and share it with every viewer:
// pdf.ts
import { localEngine } from '@embedpdf/engine';
export const engine = localEngine();Creating the engine is instant and allocates nothing. The first viewer that uses it starts it, and PDFium boots in a Web Worker in the background.
<Viewer> can also own the engine. Pass a function, and the viewer creates the engine when it
mounts and destroys it when it unmounts:
<script lang="ts">
import { Viewer } from '@embedpdf/svelte/runtime';
import { localEngine } from '@embedpdf/engine';
import { plugins } from './pdf';
</script>
<Viewer engine={() => localEngine()} {plugins} />An engine you pass in yourself is never destroyed by the viewer, so one engine can serve every viewer in your app.
Register the plugins#
Plugins are a list you create once, next to the engine:
import { stagePlugin } from '@embedpdf/svelte/stage';
import { renderPlugin } from '@embedpdf/svelte/render';
import { interactionPlugin } from '@embedpdf/svelte/interaction';
import { selectionPlugin } from '@embedpdf/svelte/selection';
export const plugins = [stagePlugin(), renderPlugin(), interactionPlugin(), selectionPlugin()];The viewer reads engine and plugins once, when it mounts, and ignores later changes to them.
Order doesn’t matter, and a plugin that needs another says so when the app starts: the annotation plugin, for example, needs the interaction plugin.
Server-side rendering#
The headless packages render nothing on the server that needs the engine: the engine only starts in the browser. So a module-level engine is safe everywhere.
In SvelteKit, render the viewer in the browser only, for example behind {#if browser} with
browser from $app/environment:
<!-- src/routes/contract/+page.svelte -->
<script lang="ts">
import { browser } from '$app/environment';
import DocumentViewer from '$lib/DocumentViewer.svelte';
</script>
{#if browser}
<DocumentViewer />
{:else}
<div style="height: 600px">Loading the viewer…</div>
{/if}To leave a whole page out of server rendering, add export const ssr = false to its +page.ts.
Bundlers and self-hosting#
The engine’s worker and WebAssembly ship inside the package, and your build picks them up.
Vite, and so SvelteKit, needs no configuration, and neither do webpack 5 and Rspack.
There’s nothing to copy, and nothing is fetched from a CDN. The engine loads from your own site, so it works offline and behind a firewall.
If your setup can’t bundle a worker, for example with a strict Content Security Policy, host the files yourself: Self-hosting lists every option, with notes for Parcel and plain esbuild.
Fonts#
Text the engine draws, such as a text box or a filled form field, needs a font with its characters. For languages the built-in fonts don’t cover, give the engine a fallback font:
export const engine = localEngine({
fallbackFonts: [{ key: 'noto-cjk', familyName: 'Noto Sans CJK', url: '/fonts/NotoSansCJK.ttf' }],
});Fonts covers registering fonts and using them by name. Every other engine option is in the engine’s Setup.
Good to know#
One viewer, many documents#
A viewer can hold several open documents at once, like tabs. See Several documents.
Changing engine or plugins#
Both are read once. To use another engine or another set of plugins, mount a new <Viewer>, for
example inside a {#key} block.
The ready-made Viewer uses these same packages. If you start there, everything on these pages still applies when you move to headless.
Your feedback goes directly to the documentation team.