Skip to main content

Quickstart

By the end of this page the Lettrove editor is open in a React app on your machine, a design is saved, and you have its HTML. Everything below runs exactly as written.

Not using React? There is a page each for Next.js, Vue, Angular, SvelteKit and plain HTML (a script tag, no build step). Each is complete on its own.

You need: a Lettrove account and Node.js 20.19 or newer (check with node --version).

1. Create a project and copy its test keys​

  1. Sign in at lettrove.com and open Settings → Embed.
  2. Type a name for your project (if you already have one, click New project first), and under What your users make choose Emails. Click Make project.
  3. You now see two test keys. Copy both somewhere safe, then click I have stored it safely:
    • the secret key, lt_sk_test_…. It is shown once. It belongs on your server only.
    • the publishable key, lt_pk_test_…. It is public; it goes in your page.

Test keys open the editor on localhost with no other setup, and they are never billed.

2. The token server​

The editor opens with a short-lived token. Your server mints it with the secret key, so the secret key never reaches a browser. This is one small Express server; in your product it is one route in the server you already have.

mkdir -p lettrove-quickstart/server
cd lettrove-quickstart/server
npm init -y
npm install express @lettrove/node

Create a file called .env with your secret key in it:

server/.env
LETTROVE_SECRET_KEY=lt_sk_test_paste_yours_here

Then the server itself:

server/server.mjs
import express from 'express';
import { Lettrove } from '@lettrove/node';

const lettrove = new Lettrove({ secretKey: process.env.LETTROVE_SECRET_KEY });
const app = express();

// The page asks this route for a token whenever the editor needs one.
app.post('/lettrove-token', async (req, res) => {
// In your product: your own id for the signed-in person, from your session.
const userId = 'demo-user';
try {
const { token } = await lettrove.tokens.create({
user: { id: userId },
origin: req.get('origin'), // the page the editor opens on
});
res.set('Cache-Control', 'no-store').json({ token });
} catch (error) {
console.error(error);
res.status(500).json({ error: error.message, code: error.code });
}
});

app.listen(3000, () => console.log('Token server on http://localhost:3000'));

Start it, and leave it running:

node --env-file=.env server.mjs

3. The React app​

In a second terminal, from the lettrove-quickstart folder:

npm create vite@latest app -- --template react-ts
cd app
npm install
npm install @lettrove/react

Send the app's /lettrove-token requests to the token server:

app/vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [react()],
server: { proxy: { '/lettrove-token': 'http://localhost:3000' } },
});

Replace src/index.css so the editor can fill the window:

app/src/index.css
html, body, #root { margin: 0; height: 100%; }

And replace src/App.tsx. Put your publishable key where it says so.

app/src/App.tsx
import { useRef } from 'react';
import { LettroveEditor, type EditorHandle } from '@lettrove/react';

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

export default function App() {
const editor = useRef<EditorHandle | null>(null);

async function exportHtml() {
const email = await editor.current?.exportHtml();
console.log(email?.html);
}

return (
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<button onClick={exportHtml}>Export HTML</button>
<LettroveEditor
ref={editor}
publishableKey="lt_pk_test_paste_yours_here"
getToken={getToken}
onDesignSaved={({ designId }) => console.log('saved', designId)}
style={{ flex: 1 }}
/>
</div>
);
}

4. Run it​

npm run dev

Open the address it prints (http://localhost:5173). The editor loads in the page.

  1. In the panel on the left, click Heading (or drag it onto the email). A heading appears; click it to type your own words.

  2. Open your browser's console (in Chrome: View → Developer → JavaScript Console). Within a moment of your edit it shows:

    saved 01J9ZQ3W8D2K7M5T1V4XG6HB0R

That id is the design. Lettrove saves as your user works; in your product you store the id with your own record (an email campaign, say) and pass it back as designId to open the same design again.

5. Get the email out​

Click Export HTML. The console prints the whole email as HTML, compiled for Gmail, Outlook and Apple Mail, ready to send with any email provider. The same call gives you email.subject, the plain-text version in email.text, and email.mergeTags: tags such as {{unsubscribe_url}} for you to fill before sending (Merge tags).

Your server can get the same email later, with the editor closed:

const email = await lettrove.designs.export(designId, { user: { id: 'demo-user' } });

You are done. Everything from here is making it yours.

If something is not right​

The editor never shows an empty box: if it cannot open, the box says why, with a code. When the box says token_invalid, your token route failed — the terminal running the token server prints the real reason. The usual first-day causes:

WhereCodeWhyFix
Terminalkey_invalidThe secret key in .env is wrong, has a space or quote around it, or was revokedCopy it again, or make a new one in Settings → Embed
Terminalorigin_not_allowedA live secret key on localhostUse the test keys while you build
TerminalError: `secretKey` is required.env was not readStart the server with node --env-file=.env server.mjs, from the server folder
Terminal`secretKey` is not a Lettrove secret keyA cut-off copy, or a space inside the keyCopy the whole key again
Editor boxtoken_invalidYour token route did not answer with a tokenRead the terminal: one of the lines above. If the terminal is quiet, the app is not reaching the server: check the proxy in vite.config.ts and that the server is running
Editor boxframe_blockedThe publishable key is wrong, or your page's Content-Security-Policy blocks the editorCheck the key; then Allowed sites and CSP

Every code is explained in Troubleshooting.

Where next​