> Lettrove docs 1.x · https://docs.lettrove.com/docs/api/embed

# `@lettrove/embed`

```bash
npm install @lettrove/embed
```

```ts
import { createEditor, LettroveEmbedError } from '@lettrove/embed';
```

With the script tag (`https://cdn.jsdelivr.net/npm/@lettrove/embed@1/dist/lettrove.iife.js`) everything
below is on the one global, `Lettrove`: `Lettrove.createEditor`, `Lettrove.LettroveEmbedError`…

## `createEditor(options)`

Opens the editor in `options.container` and resolves with an [`EditorHandle`](#editorhandle) once
it is on screen. Rejects with a [`LettroveEmbedError`](#lettroveembederror) when it cannot open.

```ts
async function getToken() {
  const response = await fetch('/lettrove-token', { method: 'POST' });
  return (await response.json()).token;
}

const editor = await createEditor({
  container: '#editor',
  publishableKey: 'lt_pk_test_…',
  getToken,
});
```

### Options

| Option | Type | Default | |
|---|---|---|---|
| `container` | `HTMLElement \| string` | required | The element the editor fills, or a CSS selector. Give it a height. |
| `publishableKey` | `string` | required | `lt_pk_test_…` or `lt_pk_live_…`. A secret key here is refused. |
| `getToken` | `() => Promise<string>` | required | Resolves with a fresh token string from your server. Called when the editor opens and before each token expires. |
| `designId` | `string \| null` | new design | The design to open. |
| `release` | `string` | this package's line (`'1'`) | The editor release: `'1'`, `'1.2'`, `'1.2.3'`, `'stable'`. [Releases](/docs/editor/releases) |
| `signal` | `AbortSignal` | — | Aborting removes the editor; `createEditor` then rejects with `editor_destroyed`. |
| `on` | `{ [event]: listener }` | — | Listeners attached before the editor opens. |
| `mode` | `'email' \| 'page' \| 'popup' \| 'document'` | the project's | A check: a different mode than the project's is refused (`mode_unavailable`). |
| `tabs` | `{ blocks?, layers?, brand?, inspector?, images?, history?: boolean }` | all on | `false` removes a panel. |
| `tools.disabled` | `ToolName[]` | `[]` | Tools the palette does not offer. |
| `tools.order` | `ToolName[]` | default order | The palette's order; tools left out follow. |
| `features.undoRedo` | `boolean` | `true` | Undo and redo. |
| `features.preview.mobile` | `boolean` | `true` | The phone preview. |
| `features.ai.images` | `boolean` | the project's | Picture generation. |
| `features.themeSwitch` | `boolean` | `true` | The editor's own light / dark switch. |
| `features.download` | `boolean` | `true` | The Download menu. |
| `branding.poweredBy` | `boolean` | `false` | Adds "Powered by Lettrove". Your name and logo are set in the dashboard. |
| `appearance.theme` | `'light' \| 'dark' \| 'auto'` | `'auto'` | |
| `appearance.variables` | [`ThemeTokens`](#themetokens) | — | The light theme's tokens. |
| `appearance.darkVariables` | [`ThemeTokens`](#themetokens) | — | The dark theme's tokens. |
| `appearance.panels.palette.dock` | `'left' \| 'right'` | `'left'` | |
| `appearance.panels.inspector.dock` | `'left' \| 'right'` | `'right'` | |
| `storage.images` | `'lettrove' \| 'host'` | `'lettrove'` | `'host'`: your `providers` store your users' uploads. [Your own upload handler](/docs/data/own-upload-handler) |
| `providers['image:upload']` | `(file, { signal }) => Promise<HostedImage \| ProviderFailure>` | — | Required with `storage.images: 'host'`. |
| `providers['image:list']` | `({ query, cursor, limit }, { signal }) => Promise<{ items, nextCursor } \| ProviderFailure>` | — | Your library, for the Images panel. |
| `storage.designs` | `'lettrove' \| 'host'` | `'lettrove'` | `'host'`: your `providers` keep your users' designs, in your database. [Your own design storage](/docs/data/own-design-storage) |
| `providers['design:load']` | `({ id }, { signal }) => Promise<{ design, revision } \| ProviderFailure>` | — | Required with `storage.designs: 'host'`. |
| `providers['design:save']` | `({ id, design, revision, idempotencyKey }, { signal }) => Promise<{ id, revision, savedAt } \| { conflict: { theirs, revision } } \| ProviderFailure>` | — | Required with `storage.designs: 'host'`. |

`ToolName` is one of `section`, `text`, `heading`, `image`, `button`, `divider`, `spacer`, `social`,
`html`, `form`. Options can only narrow what the project allows: [Options](/docs/editor/options).

### `ThemeTokens`

All optional strings: `accent`, `accentText`, `surface`, `surfaceRaised`, `text`, `textMuted`,
`border`, `focusRing`, `radius`, `fontFamily`. What each colours: [Theming](/docs/editor/theming#what-each-token-colours).

## `EditorHandle`

| Member | |
|---|---|
| `design: { designId, revision }` | The design that is open. |
| `release: string` | The editor release running, e.g. `'1.0.0'`. |
| `on(event, listener): () => void` | Listen for an event; call the returned function to stop. |
| `setTheme(theme): Promise<'light' \| 'dark'>` | Switch theme while open; resolves with what it shows. |
| `exportHtml(options?): Promise<ExportResult>` | The design as on screen, as HTML (and chunks, text, subject…). |
| `exportPlainText(options?): Promise<PlainTextResult>` | As plain text. |
| `exportImage(options?): Promise<FileResult>` | As a PNG (`fullPage`, default `true`). |
| `exportPdf(options?): Promise<FileResult>` | As a PDF. |
| `exportZip(options?): Promise<FileResult>` | As a ZIP. |
| `getDesign(): Promise<DesignSnapshot>` | The design document: `{ designId, revision, design }`. |
| `destroy(): void` | Removes the editor and everything it set up. Safe to call twice. |

After `destroy`, every method rejects with `editor_destroyed`. Options and results of each export:
[Getting designs out](/docs/editor/exports).

### `ExportResult`

`exportId`, `designId`, `revision`, `mode`, `html`, `chunks` (`{ body, css, js, fonts }`), `text`,
`subject`, `preheader`, `mergeTags`, `rendererVersion`, `bytes`, `warnings`, `design`.

### `FileResult`

`exportId`, `designId`, `revision`, `mode`, `format` (`'png' | 'pdf' | 'zip'`), `url`, `expiresAt`
(`string | null`), `filename`, `bytes`, `warnings`, `design`.

### `HostedImage`

What an image provider answers: `url` (required, absolute `https://`), and optionally `width`,
`height`, `alt`, `mime`, `bytes`, `id` (your own id, kept on the image in the design).
`ProviderFailure` is `{ error: { message, code? } }`.

### `MergeValues`

`{ contact?: Record<string, string>; unsubscribeUrl?: string; preferencesUrl?: string }` — the `merge`
option of every export. [Merge tags](/docs/editor/merge-tags)

## Events

`ready`, `design:loaded`, `design:updated`, `design:saved`, `selection:changed`, `image:uploaded`,
`theme:changed`, `config:adjusted`, `error`. Every payload: [Events](/docs/editor/events).

## `LettroveEmbedError`

```ts
try {
  await createEditor({ /* … */ });
} catch (e) {
  if (e instanceof LettroveEmbedError) console.error(e.code, e.message); // e.g. 'frame_blocked'
}
```

`code` is one of the [error codes](/docs/api/errors); `message` says what to fix; `docsUrl` links to
that code's row on the error page, for your logs.

## Other exports

| Export | |
|---|---|
| `LOADER_VERSION` | This package's version, e.g. `'1.0.0'`. |
| `DEFAULT_RELEASE` | The release opened when you pass none (`'1'` for a 1.x package). |
| `DEFAULT_EMBED_ORIGIN` | `'https://lettrove-embed.com'`. |
| Types | `CreateEditorOptions`, `EditorOptions`, `EditorHandle`, `EditorEvents`, `EventName`, `ErrorCode`, `ExportOptions`, `ExportResult`, `PlainTextOptions`, `PlainTextResult`, `ImageOptions`, `FileExportOptions`, `FileResult`, `HtmlChunks`, `MergeValues`, `TextOptions`, `DesignRef`, `DesignSnapshot`, `DesignDocument`, `Mode`, `TabName`, `Theme`, `ThemeTokens`, `ToolName`, `Dock`, `Providers`, `ProviderContext`, `ProviderFailure`, `HostedImage` |
