Skip to content

All events bubble from the <email-builder> element. Use addEventListener or framework equivalents.


eb:save

Fired when the user clicks Save or when builder.save() is called programmatically.

js
builder.addEventListener('eb:save', (e) => {
  const { json } = e.detail; // BlockTree
  await saveToDatabase(json);
});
e.detail fieldTypeDescription
jsonBlockTreeThe full block tree at the time of save

eb:export

Fired when the user clicks Export HTML (Pro+ plan). Receives compiled, email-client-safe HTML.

js
builder.addEventListener('eb:export', (e) => {
  const { html } = e.detail; // string
  await sendWithSendGrid({ html, to: recipient });
});
e.detail fieldTypeDescription
htmlstringCompiled HTML ready to send via any ESP

eb:error

Fired when a recoverable error occurs (compilation failure, save failure, load failure).

js
builder.addEventListener('eb:error', (e) => {
  const { code, message } = e.detail;
  showToast(`Editor error: ${message}`, 'error');
});
e.detail fieldTypeDescription
codestringOne of: COMPILE_FAILED, SAVE_FAILED, LOAD_FAILED, EXPORT_FAILED
messagestringHuman-readable description

Using callbacks instead

You can pass the same handlers as callbacks in updateConfig — use whichever style fits your framework:

js
builder.updateConfig({
  onSave:   (json) => saveToDatabase(json),
  onExport: (html) => sendEmail(html),
  onClose:  ()     => router.back(),
});

Both the DOM event and the callback fire — you don't need to choose one.


React example

jsx
import { useRef, useEffect } from 'react';
import '@maillune/sdk';

export function EmailEditor({ userId, onSave }) {
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    const handler = (e) => onSave(e.detail.json);
    el.addEventListener('eb:save', handler);
    return () => el.removeEventListener('eb:save', handler);
  }, [onSave]);

  return (
    <div style={{ height: 700 }}>
      <email-builder ref={ref} api-key={process.env.MMS_API_KEY} user-id={userId} />
    </div>
  );
}