SvelteKit
The editor in a SvelteKit app: one server route mints tokens, one page opens the editor with
createEditor from @lettrove/embed. One process, four small files. Written for SvelteKit 3.
You need a project's two test keys from lettrove.com → Settings → Embed (step 1 of the Quickstart), and Node.js 22.12 or newer (what SvelteKit asks for).
1. Create the app and install two packages
npx sv create lettrove-sveltekit --template minimal --types ts --no-add-ons --install npm
cd lettrove-sveltekit
npm install @lettrove/embed @lettrove/node
@lettrove/embed opens the editor, in the browser. @lettrove/node mints tokens with your secret
key, on the server only.
2. Your secret key
Create .env in the app's folder:
LETTROVE_SECRET_KEY=lt_sk_test_paste_yours_here
SvelteKit reads only the variables you declare. Declare this one in src/env.ts; a variable not
marked public is handed to server code only, so the key never reaches a browser.
import { defineEnvVars } from '@sveltejs/kit/env';
export const variables = defineEnvVars({
LETTROVE_SECRET_KEY: {},
});
3. The token route
The editor opens with a short-lived token. Create src/routes/lettrove-token/+server.ts:
import { json } from '@sveltejs/kit';
import { Lettrove } from '@lettrove/node';
import { LETTROVE_SECRET_KEY } from '$app/env/private';
import type { RequestHandler } from './$types';
const lettrove = new Lettrove({ secretKey: LETTROVE_SECRET_KEY });
export const POST: RequestHandler = async ({ request }) => {
// In your product: your own id for the signed-in person, from your session (locals).
const userId = 'demo-user';
const { token } = await lettrove.tokens.create({
user: { id: userId },
origin: request.headers.get('origin')!, // the page the editor opens on
});
return json({ token }, { headers: { 'Cache-Control': 'no-store' } });
};
4. The page
Replace src/routes/+page.svelte. Put your publishable key where it says so.
<script lang="ts">
import { onMount } from 'svelte';
import { createEditor, type EditorHandle } from '@lettrove/embed';
let container: HTMLDivElement;
let editor: EditorHandle | null = null;
async function getToken() {
const response = await fetch('/lettrove-token', { method: 'POST' });
return (await response.json()).token;
}
onMount(() => {
createEditor({
container,
publishableKey: 'lt_pk_test_paste_yours_here',
getToken,
on: { 'design:saved': ({ designId }) => console.log('saved', designId) },
}).then((opened) => (editor = opened));
return () => editor?.destroy();
});
async function exportHtml() {
const email = await editor?.exportHtml();
console.log(email?.html);
}
</script>
<div class="page">
<button onclick={exportHtml}>Export HTML</button>
<div bind:this={container} class="editor"></div>
</div>
<style>
:global(body) { margin: 0; }
.page { height: 100vh; display: flex; flex-direction: column; }
.editor { flex: 1; }
</style>
5. Run it
npm run dev
Open the address it prints (http://localhost:5173). Add a Heading from
the panel on the left; the browser's console prints saved and the design's id. Keep that id to
open the same design again: pass it as designId. Click Export HTML and the console prints the
whole email, compiled for every inbox.
Next
- If the box shows a code instead of the editor: the Quickstart's checklist.
@lettrove/embed: every option and method ofcreateEditor, and the events.- Getting designs out · Theming · Test and live keys.