Next.js
Sign the user id in a server component and render the editor iframe on the same request.
In the App Router a server component can sign and render in one pass. The Plugin Secret is read from process.env on the server and never reaches the browser.
Before you start
Follow the first three steps of the quickstart: create a plugin, add localhost:3000 to its allowed domains, and copy the Plugin ID and Secret.
Configuration
MAILEDITOR_PLUGIN_ID=9f3c1e7a-4b21-4c8e-9a77-1d2f5b6c8e10
MAILEDITOR_PLUGIN_SECRET=your_plugin_secret
MAILEDITOR_EDITOR_BASE_URL=https://maileditor.net
MAILEDITOR_PLUGIN_VERSION=1.0.0Do not prefix these with NEXT_PUBLIC_. That would ship the secret to the browser.
Sign and render
import { createHmac } from 'node:crypto'
export const dynamic = 'force-dynamic'
export default function EditorPage() {
const userId = 'customer-user-4471'
const expires = Math.floor(Date.now() / 1000) + 300
const payload = `v1|${Buffer.byteLength(userId)}:${userId}|${expires}`
const signature = createHmac('sha256', process.env.MAILEDITOR_PLUGIN_SECRET!)
.update(payload)
.digest('hex')
const params = new URLSearchParams({
external_user_id: userId,
identity_expires: String(expires),
identity_signature: signature,
plugin_version: process.env.MAILEDITOR_PLUGIN_VERSION!,
})
const src = `${process.env.MAILEDITOR_EDITOR_BASE_URL}/plugin-editor/v1/${process.env.MAILEDITOR_PLUGIN_ID}?${params}`
return (
<iframe
src={src}
title="Email editor"
allow="clipboard-write; fullscreen"
style={{ width: '100%', height: '100vh', border: 0 }}
/>
)
}Replace userId with the id of the signed-in user from your own session. force-dynamic makes Next.js render the page on every request, so the signature is always fresh and is never baked in at build time.
If you would rather sign in a route handler and build the URL in a client component, that variant is in the quickstart.
Next steps
- Add
template_idto the query string to open one of the user's saved templates. See Embedding the editor. - If the iframe shows "Plugin editor unavailable", read
result.errorin thesessionresponse. See Error codes.

.png)