@lettrove/vue
| Install | |
|---|---|
npm install @lettrove/vue |
|
| Latest Version: | 1.1.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 13, 2026 |
| Links: | npm |
@lettrove/vue
The embeddable drag-and-drop email editor and page builder, as a Vue 3 component. Your users design emails, landing pages, popups and documents inside your product, under your name and logo, with AI built in, and with HTML that renders in every inbox.
Website · Docs · Vue guide · Your projects and keys · Changelog · Support
This README describes the version of @lettrove/vue you installed: it is published with it.
<LettroveEditor /> is built on @lettrove/embed,
which it installs. The editor itself loads from https://lettrove-embed.com, so this package stays
small and fixes reach your users without a reinstall. Vue 3.3 or newer, and Nuxt.
What you get
- Four kinds of design in one editor: emails, landing pages, popups and print-ready documents.
- Your product, not ours: your logo and name, your colours through theme tokens, light and dark.
- Email that works: compiled for every major email client, images hosted, merge tags with fallbacks.
- Every way out: HTML, plain text, PNG, PDF and ZIP, from the page or later from your server.
- Saved as your users work, with restore points.
- Made for Vue: one component, one emitted event per editor event, the editor handle on a template ref, and safe under server rendering.
Before you start
- Sign up at lettrove.com and open Settings → Embed.
- Create a project and copy its two test keys: the publishable key (
lt_pk_test_…, for your page) and the secret key (lt_sk_test_…, for your server). - Add a route on your server that mints editor tokens with the secret key, using
@lettrove/node. The secret key never reaches the browser.
Test keys work on localhost, so nothing needs adding to the allowed sites while you build.
Quick start
npm install @lettrove/vue
Your server, e.g. POST /lettrove-token behind your own login (in Nuxt, a server route):
import { Lettrove } from '@lettrove/node';
const lettrove = new Lettrove({ secretKey: process.env.LETTROVE_SECRET_KEY! });
export async function POST(request: Request) {
const user = await currentUser(request); // yours
const { token } = await lettrove.tokens.create({ user: { id: user.id }, origin: request.headers.get('origin')! });
return Response.json({ token }, { headers: { 'cache-control': 'no-store' } });
}
Your page:
<script setup lang="ts">
import { ref } from 'vue';
import { LettroveEditor } from '@lettrove/vue';
const savedDesignId = ref<string | undefined>(undefined);
async function getToken() {
const response = await fetch('/lettrove-token', { method: 'POST' });
return (await response.json()).token;
}
</script>
<template>
<LettroveEditor
publishable-key="lt_pk_test_…"
:get-token="getToken"
:design-id="savedDesignId"
@design-saved="({ designId }) => (savedDesignId = designId)"
style="height: 720px"
/>
</template>
Lettrove saves the design as your user works. Keep the designId from @design-saved to open the
same design next time.
The whole thing step by step: Vue.
Props
Every option of createEditor from @lettrove/embed is a prop, except container, signal and
on, which the component provides. In a template, write them in kebab-case and bind values with
:.
| Prop | |
|---|---|
publishable-key |
Required. lt_pk_test_… or lt_pk_live_…. |
get-token |
Required. Resolves with a fresh token string from your server. |
design-id |
The design to open. Left out, a new one is started. |
release |
The editor release: '1' (the default line), '1.2', '1.2.3' or 'stable'. |
mode |
'email', 'page', 'popup' or 'document': a check against the project's mode. |
tabs |
{ blocks, layers, brand, inspector, images, history }: false removes a panel. |
tools |
{ disabled, order }: which tools the palette offers, and in what order. |
features |
{ undoRedo, preview: { mobile }, ai: { images }, themeSwitch, download }. |
branding |
{ poweredBy }. Your logo and name are set on the project, not here. |
appearance |
{ theme, variables, darkVariables, panels }: theme tokens and docking. |
storage, providers |
Keep your users' images or designs in your own storage. |
class and style go to the element the editor fills: give it a height.
Every option, its type and its default:
@lettrove/embed options. Options can only
narrow what the project allows.
<LettroveEditor
publishable-key="lt_pk_test_…"
:get-token="getToken"
:tools="{ disabled: ['html'] }"
:features="{ download: false }"
:appearance="{ theme: 'auto', variables: { accent: '#5b3df5', radius: '10px' } }"
style="height: 100vh"
/>
Events
| Event | Payload |
|---|---|
@ready |
The editor handle, once the editor is on screen |
@error |
A LettroveEmbedError (code, message, docsUrl): it could not open, or reported an error while open |
@design-loaded |
{ designId, designVersion, upgradedFrom?, hasNewerBlocks? } |
@design-updated |
{ design, revision, change: { kind, blockId? } }; kind is edit, undo, redo, restore or assistant |
@design-saved |
{ designId, revision } |
@selection-changed |
{ blockId, type }, both null when nothing is selected |
@image-uploaded |
{ url, width?, height?, id? } |
@theme-changed |
{ theme, applied } |
@config-adjusted |
{ option, reason }: an option was dropped or changed |
Every payload, field by field: Events.
The editor handle on a template ref
A template ref to the component is a LettroveEditorInstance. Its editor is the editor handle
once the editor is open, and null before and after.
<script setup lang="ts">
import { ref } from 'vue';
import { LettroveEditor, type LettroveEditorInstance } from '@lettrove/vue';
const designer = ref<LettroveEditorInstance | null>(null);
async function exportEmail() {
const email = await designer.value?.editor?.exportHtml({ merge: { contact: { first_name: 'Ada' } } });
if (email) await sendWithMyProvider(email.subject, email.html);
}
</script>
<template>
<button @click="exportEmail">Send</button>
<LettroveEditor ref="designer" publishable-key="lt_pk_test_…" :get-token="getToken" style="height: 720px" />
</template>
Member of editor |
|
|---|---|
design |
{ designId, revision } |
release |
The editor release running |
exportHtml(options?) |
The design as on screen, as HTML to send, with subject, text, mergeTags and warnings |
exportPlainText, exportImage, exportPdf, exportZip |
Plain text, or a PNG, PDF or ZIP kept for you to fetch |
getDesign() |
The design document, to keep your own copy |
setTheme(theme) |
Switch light or dark while open |
on(event, listener) |
Listen for an event; returns a function that stops listening |
The same handle in full, with every export's options and results:
@lettrove/embed and
Getting designs out.
Behaviour you can rely on
- Server rendering (Nuxt): it renders an empty element, and the editor opens in the browser.
- Changing props: inline functions and objects may change on every render without reopening the
editor. A new
publishable-key,design-id,releaseor set of options opens it again. A newappearance.themeswitches the open editor without reopening. - An attribute that is not a prop (
featurs, say) reaches the editor, which names it: refused with a test key, a warning with a live one. It is never silently lost. - Unmounting removes the editor and everything it set up.
Errors
@error receives a LettroveEmbedError with a stable code, a message saying what to fix and a
docsUrl. When the editor cannot open, the element shows the same reason instead of an empty box.
| Code | Usually |
|---|---|
options_invalid |
A prop is wrong; the message names it. With a test key the editor stops, so you catch it while you build |
frame_blocked |
The site is not on the project's allowed sites, or your Content-Security-Policy needs frame-src https://lettrove-embed.com |
token_invalid |
get-token threw, or resolved with something other than the token string |
origin_not_allowed |
The token was minted for a different site than the page's |
key_invalid |
The publishable key is wrong or revoked |
Every code: Error codes. What to check first: Troubleshooting.
Versions
@lettrove/vue, @lettrove/embed, @lettrove/react and @lettrove/node share one version number
and are published together, so @lettrove/vue@1.4.2 is built for @lettrove/embed@1.4.2. Within
1.x nothing is taken away and no prop changes meaning. Release candidates are published under the
next tag and open with test keys only.
More in Releases and pinning.
TypeScript
Types are included. The package exports LettroveEditor, LettroveEditorInstance and
LettroveEmbedError, and the types of @lettrove/embed: EditorHandle, ExportResult,
FileResult, EditorEvents, ErrorCode, ThemeTokens and the rest.
Documentation
The docs for this major version, at docs.lettrove.com:
- Start: How it fits together · Vue
- The editor: Options · Theming and white label · Events · Getting designs out · Merge tags
- Your data: Your own upload handler · Your own design storage
- Going live: Test and live · Allowed sites and CSP
- Reference:
@lettrove/vue·@lettrove/embed· Error codes · Changelog
Support
Questions, problems and security reports: support@lettrove.com.
Licence
MIT, for this package. The Lettrove editor it opens is a hosted service under the Lettrove terms; none of it is in this package.
Related Packages
Vue 3 bindings for the email builder. A thin view layer over @email-builder/engi...
A visual drag & drop email builder for Vue 3. Exports email-client-compatible HT...
An open-source, framework-agnostic email template editor. Drag-and-drop block ed...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 1.1.0 | >=3.3 | MIT | |
| 1.0.1 | >=3.3 | MIT | |
| 1.0.0 | >=3.3 | MIT |
Pre-releases (3)
| Version | Released | Vue | License |
|---|---|---|---|
| 1.0.0-rc.1 | >=3.3 | MIT | |
| 1.0.0-rc.0 | >=3.3 | MIT | |
| 0.0.0-stage |