---
title: "Setup"
description: "Install the local engine, create it once for your app, and run it in any bundler, with server-side rendering, or in Node."
source: "https://www.embedpdf.com/docs/engine/setup"
---

# Setup

```sh
pnpm add @embedpdf/engine@next
```

## Create the engine once

Create one engine when your app starts and share it everywhere:

```ts
// pdf.ts
import { localEngine } from '@embedpdf/engine';

export const engine = localEngine();
```

`localEngine()` returns right away and does no work yet. The first `open()` starts PDFium in a
Web Worker; calls you make while it starts wait for it, so you never check whether it's ready.
To start it early, for example while the rest of your app loads, call `engine.warmup()`.

One engine holds any number of open documents. Close each document when you're done with it
(`doc.close()`), and keep the engine for the life of your app.

## Bundlers

The engine's worker and its WebAssembly file ship inside the package. Vite, webpack 5 (and
Next.js), Rollup and Parcel pick them up without any configuration: there's nothing to copy and
nothing is fetched from a CDN.

If your setup can't bundle a worker (a strict Content Security Policy, an older bundler), host
the files yourself: [Self-hosting](https://www.embedpdf.com/docs/viewer/self-hosting) lists every option.

## Server-side rendering

A module-level `localEngine()` is safe in Next.js, Nuxt and SvelteKit: it does nothing until you
open a document, and that only happens in the browser. In the Next.js App Router, mark the
component that opens documents with `'use client'`.

## Node.js

The same call works in Node. There the engine runs PDFium in the same process, natively when
your platform has a build and as WebAssembly otherwise:

```ts
import { readFile, writeFile } from 'node:fs/promises';
import { localEngine } from '@embedpdf/engine';

const engine = localEngine();
const doc = await engine.open({ kind: 'bytes', bytes: await readFile('in.pdf') });

// …change the document…

await writeFile('out.pdf', await doc.download());
await doc.close();
await engine.destroy();
```

In a script, destroy the engine at the end so the process can exit.

Rendering works in Node without a canvas: `render.image()` gives you PNG or BMP bytes. WebP needs a
canvas, so in Node pass your own `imageEncoder` (for example one built on `sharp`) if you need it.
To pick the runtime yourself, pass `runtime: { prefer: 'wasm' }` or `{ prefer: 'native' }`.

## Fonts

Text the engine draws (a free text annotation, a filled form field) needs a font that has its
characters. The engine covers Latin, Hebrew and Arabic text on its own. For other languages, give
the engine a fallback font when you create it:

```ts
const engine = localEngine({
  fallbackFonts: [{ key: 'noto-cjk', familyName: 'Noto Sans CJK', url: '/fonts/NotoSansCJK.ttf' }],
});
```

The font is loaded while the engine starts, so it's ready before the first document opens.
[Fonts](https://www.embedpdf.com/docs/engine/documents/fonts) covers registering fonts later and using them by name.

Hebrew and Arabic come from Noto Sans Hebrew and Noto Naskh Arabic, which the engine loads while it
starts too (about 320 KB, as chunks of your build), and tries after your fallback fonts. If your
users don't write Hebrew or Arabic, leave them out:

```ts
const engine = localEngine({ defaultFallbackFonts: false });
```

## With a viewer

The viewer can use your engine or create its own:

- `<Viewer engine={engine} />` uses your engine and never destroys it. Share one engine across
  every viewer in your app.
- `<Viewer engine={() => localEngine()} />` lets the viewer create an engine when it mounts and
  destroy it when it unmounts.

> Building your own viewer interface? The [headless documentation](https://www.embedpdf.com/docs/headless/quick-start)
> wires the engine to pages, selection and annotation tools for you.

## Options

| Option                 | What it does                                                                                                                                                             |
| :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fallbackFonts`        | Fonts used for characters the document's own fonts don't have.                                                                                                           |
| `fonts`                | Fonts you use by name only (a free text `fontFamily`), not as fallback.                                                                                                  |
| `defaultFallbackFonts` | `false` leaves out the Hebrew and Arabic fonts the engine comes with.                                                                                                    |
| `worker`               | Your own worker: a `Worker`, a function returning one, a URL, or `'inline'`.                                                                                             |
| `wasmUrl`              | Where to load the WebAssembly file from, when you host it yourself.                                                                                                      |
| `renderPolicy`         | The render sizes documents answer with; see [Render policy](https://www.embedpdf.com/docs/engine/documents/rendering#render-policy).                                     |
| `imageEncoder`         | Your own image encoder, for formats the engine can't encode where it runs (WebP in Node).                                                                                |
| `signedDocumentPolicy` | `'permit'` stops the engine refusing what signatures forbid; see [Turning protection off](https://www.embedpdf.com/docs/engine/forms/signatures#turning-protection-off). |
| `runtime`              | In Node: `{ prefer: 'native' }` or `{ prefer: 'wasm' }`. The engine picks native when it can.                                                                            |
