Next.js
The Lettrove editor in a Next.js app (App Router): one route handler mints tokens on your server, one client component shows the editor. Four files in all, starting from a new app.
You need a project's two test keys from lettrove.com → Settings →
Embed (step 1 of the Quickstart).
Test keys also work on localhost, so nothing needs adding to the allowed sites while you build.
1. Create the app and install two packages
npx create-next-app@latest lettrove-next --yes --ts --app --no-src-dir --use-npm
cd lettrove-next
npm install @lettrove/react @lettrove/node
@lettrove/react is the editor component, for the browser. @lettrove/node mints tokens with your
secret key, on your server only.
2. Your keys
Create .env.local in the app's folder:
LETTROVE_SECRET_KEY=lt_sk_test_paste_yours_here
NEXT_PUBLIC_LETTROVE_PUBLISHABLE_KEY=lt_pk_test_paste_yours_here
The secret key has no NEXT_PUBLIC_ prefix, so Next.js keeps it on the server: it never reaches a
browser. The publishable key is meant for the page.
3. The token route
The editor opens with a short-lived token. Create app/lettrove-token/route.ts:
import { Lettrove, LettroveApiError } from '@lettrove/node';
export async function POST(request: Request) {
// In your product: your own id for the signed-in person, from your session. Never from the request body.
const userId = 'demo-user';
try {
const lettrove = new Lettrove({ secretKey: process.env.LETTROVE_SECRET_KEY! });
const { token } = await lettrove.tokens.create({
user: { id: userId },
origin: request.headers.get('origin')!, // the page the editor opens on
});
return Response.json({ token }, { headers: { 'Cache-Control': 'no-store' } });
} catch (error) {
console.error(error);
const code = error instanceof LettroveApiError ? error.code : 'token_route_failed';
return Response.json({ error: 'No token', code }, { status: 500 });
}
}
4. The editor
getToken is a function, and Next.js does not pass functions from a server component to the
browser, so the editor lives in a small client component. Create app/email-editor.tsx:
'use client';
import { LettroveEditor } from '@lettrove/react';
async function getToken() {
const response = await fetch('/lettrove-token', { method: 'POST' });
return (await response.json()).token;
}
export function EmailEditor() {
return (
<LettroveEditor
publishableKey={process.env.NEXT_PUBLIC_LETTROVE_PUBLISHABLE_KEY!}
getToken={getToken}
onDesignSaved={({ designId }) => console.log('saved', designId)}
style={{ height: '100vh' }}
/>
);
}
Then replace app/page.tsx with:
import { EmailEditor } from './email-editor';
export default function Home() {
return <EmailEditor />;
}
5. Run it
npm run dev
Open http://localhost:3000. The editor appears; add a heading, and the
browser's console prints saved and the design's id. Keep that id to open the same design again:
pass it as designId.
On the server, npm run build then npm start work as they are: the page renders an empty box on
the server, and the editor opens in the browser.
Next
- Getting designs out:
ref.current.exportHtml()gives the email as HTML to send. - Theming and white label: your colours, light and dark, your name and logo.
- Test and live keys: add your site before you switch to live keys.
- If the box shows a code instead of the editor: Troubleshooting.