Skip to main content

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:

.env.local
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:

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:

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:

app/page.tsx
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​