Quick start
Add the loader, give the editor an element with a height, and mount it with your public key. You find the key in your dashboard under Plugin.
<div id="editor" style="height: 720px"></div>
<script src="https://customer.mailblocks.app/sdk/v1.js"></script>
<script>
const editor = Mailblocks.mount('#editor', {
publicKey: 'pk_live_…',
user: { id: 'user-42' },
design: savedDesign, // optional: design JSON or MJML
placeholders: [
{ label: 'Contact', items: [{ label: 'First name', value: '{{first_name}}' }] },
],
onReady: ({ plan }) => console.log('Editor ready on', plan),
onSave: ({ html, design }) => saveTemplate({ html, design }),
onError: ({ code, message }) => console.warn(code, message),
});
</script>React and Vue
The SDK has no dependencies, so wrap it the way you wrap any widget: mount on first render, destroy on unmount.
import { useEffect, useRef } from 'react';
export function EmailEditor({ user, design, onSave }) {
const element = useRef(null);
useEffect(() => {
const editor = window.Mailblocks.mount(element.current, {
publicKey: process.env.NEXT_PUBLIC_MAILBLOCKS_KEY,
user: { id: user.id, signature: user.mailblocksSignature },
design,
onSave,
});
return () => editor.destroy();
}, []);
return <div ref={element} style={{ height: 720 }} />;
}Options
Only publicKey and user.id are required.
| Option | Type | Description |
|---|---|---|
| publicKey | string | Your licence public key (pk_live_…). Safe to use in the browser. |
| user | object | { id, signature? } identifying the person editing. The id counts towards monthly active users. |
| design | string | A saved design JSON or an MJML template to open. |
| designId | string | A stable id for the design, used to offer restoring an unsaved draft. |
| locale | string | Interface language: en, es, fr, de, pt, ro or el. |
| placeholders | array | Merge tags, flat or grouped: [{ label, items: [{ label, value, insert? }] }]. |
| theme | object | Colours, fonts and radius for the editor chrome. Applied on plans with white labelling. |
| storage | string | false | object | "mailblocks" (default), false, or { endpoints, auth } for your own image storage. |
| toolbar | object | { save?: boolean, back?: boolean } to show or hide the toolbar buttons. |
| ai | false | object | false to hide the AI builder, or { presets, sentence, texts } to customise it. |
Events
Pass callbacks as onReady, onSave and so on, or subscribe with editor.on(name, callback).
- ready
- The editor is loaded. Receives { plan, features, development }.
- save
- The user saved, or you called save(). Receives { html, design }.
- change
- The design changed since it was opened.
- error
- The editor could not start or load the design. Receives { code, message }.
- back
- The user pressed the back button.
- generated
- The AI builder produced a template. Receives { mjml }.
Methods
- save()
- Saves and resolves with { html, design }.
- load()
- Replaces the open design with a design JSON or an MJML template.
- destroy()
- Removes the editor and its listeners.
const { html, design } = await editor.save();
editor.load(previouslySavedDesign);
const stop = editor.on('change', () => markUnsaved());
stop();
editor.destroy();Signed user ids
Sign the user id with your secret key on your server and pass the signature. Turn on “Require signed user ids” in your dashboard to refuse unsigned sessions. Never put the secret key in browser code.
// Laravel, in the controller that renders the page with the editor
$signature = hash_hmac('sha256', (string) $user->id, config('services.mailblocks.secret'));
return view('templates.edit', [
'mailblocksUser' => ['id' => (string) $user->id, 'signature' => $signature],
]);Domains
Add every host name that runs the editor in your dashboard. localhost, 127.0.0.1 and *.test, *.local and *.localhost always work and are not counted. Wildcards such as *.example.com cover every subdomain and are included from the Growth plan.
Image storage
By default images are stored by Mailblocks. To use your own storage, pass endpoints compatible with the file manager protocol and allow requests from the editor origin with CORS.
How the frame is protected
The editor runs in an iframe from our domain. It renders only for the origins on your licence, talks to your page through postMessage scoped to that origin, and its code is served through short-lived signed URLs.
Error codes
- unauthorized_origin
- The page is not on a domain authorised for this licence.
- inactive_license
- The trial ended or the licence is suspended.
- active_user_limit_reached
- This month’s active user allowance is used up.
- invalid_user_signature
- Signing is required, or the signature does not match the user id.
- ticket_invalid
- The editor took too long to start; reload it.