Skip to content

HTML attributes ​

Set these directly on the <email-builder> element:

AttributeTypeRequiredDescription
api-keystring✅Your Mail Lune API key
user-idstring—ID of the end user in your system. When set, the template picker shows that user's templates alongside account-shared templates. Omit only for anonymous or read-only embeds.
template-idstring—Load and auto-save a specific template
theme'light' | 'dark' | 'auto'—Default: 'auto' (follows OS preference)
headlessboolean flag—Hide the toolbar entirely. See Headless mode.
hide-saveboolean flag—Hide only the Save button while keeping the rest of the toolbar
hide-exportboolean flag—Hide only the Export HTML button while keeping the rest of the toolbar

Runtime config (updateConfig) ​

Use updateConfig() for options that can't be set as attributes, or to update config after mount without remounting:

js
const builder = document.querySelector('email-builder');

builder.updateConfig({
  locale: 'fr',
  accentColor: '#ef4444',
  brandName: 'Acme Mailer',
  mergeTags: [
    { name: 'First name', value: '{{first_name}}', sample: 'Jane' },
    { name: 'Company',    value: '{{company}}',    sample: 'Acme Inc.' },
  ],
  onSave:   (json) => console.log('saved', json),
  onExport: (html) => sendEmail(html),
  onClose:  ()     => router.back(),
});

Full SdkConfig reference ​

ts
interface SdkConfig {
  // Required
  apiKey:  string;

  // Strongly recommended — enables the "My Templates" tab in the picker
  userId?: string;

  // Optional — set via attributes or updateConfig
  baseUrl?:        string;                    // default: Mail Lune API
  theme?:          'light' | 'dark' | 'auto'; // default: 'auto'
  locale?:         'en' | 'sv' | 'no' | 'da' | 'fr' | 'es'; // default: 'en'

  // Headless mode — hides toolbar, host supplies all controls
  headless?:       boolean;

  // White-label (any paid plan)
  accentColor?:    string;  // any CSS color: '#ef4444', 'hsl(0 72% 51%)'
  brandName?:      string;  // replaces "Email Builder" in the toolbar
  hideBranding?:   boolean; // hides the brand mark entirely

  // Merge tags — shown as insertable pills in the text editor
  mergeTags?: Array<{
    name:   string;  // display label, e.g. "First name"
    value:  string;  // inserted text, e.g. "{{first_name}}"
    sample: string;  // shown in preview, e.g. "Jane"
  }>;

  // Host-provided templates (Agency+) — see "Custom templates" below
  templates?:            SdkTemplate[];
  hideDefaultTemplates?: boolean;  // hide Maillune's built-in starters
  showSaveAsTemplate?:   boolean;  // show a "Save as template" toolbar button

  // Callbacks — also fired as DOM events (see Events)
  onSave?:         (json: BlockTree)   => void;
  onExport?:       (html: string)      => void;
  onClose?:        ()                  => void;
  onSaveTemplate?: (doc: SavedTemplate) => void;  // user saved current email as a template
}

Custom templates (Agency+) ​

On the Agency and Enterprise plans you can feed your own templates into the editor's picker via config.templates. Lower plans ignore the array.

ts
interface SdkTemplate {
  id?:        string;     // your stable id (one is generated if omitted)
  name:       string;     // shown on the card
  clientId?:  string;     // when set → grouped under the "Your templates" tab
  blockTree:  BlockTree;  // the email document
  thumbnail?: string;     // optional preview image URL
  tags?:      string[];   // optional tag pills
}
js
builder.updateConfig({
  hideDefaultTemplates: true,            // hide the "Mail Lune" starter tab
  templates: [
    // Populated clientId → "Your templates" tab (scope to one end-user)
    { id: 't1', name: 'Welcome', clientId: 'user_123', blockTree: welcomeJson },
    // No clientId → shared "Templates" tab (available to everyone)
    { id: 't2', name: 'Monthly digest', blockTree: digestJson },
  ],
});

The picker builds its tabs from what you provide:

TabContents
Your templatestemplates entries with a populated clientId
Templatestemplates entries without a clientId
Mail LuneBuilt-in starters — hidden when hideDefaultTemplates: true

Save as template ​

Set showSaveAsTemplate: true to add a Save as template button to the toolbar, or call saveAsTemplate() from your own UI. Either way you get back a document flagged template: true that you store and pass straight back via config.templates:

ts
interface SavedTemplate {
  template: true;
  name:      string;
  clientId?: string;    // mirrors the current userId, so it round-trips into "Your templates"
  blockTree: BlockTree;
}
js
builder.updateConfig({
  showSaveAsTemplate: true,
  onSaveTemplate: (doc) => {
    // doc.template === true, doc.clientId === current user-id
    await fetch('/api/templates', { method: 'POST', body: JSON.stringify(doc) });
  },
});

Headless mode ​

Headless mode removes the entire toolbar so your application can supply its own save, export, and undo controls — matching your product's design language exactly.

Activate via attribute:

html
<email-builder headless api-key="YOUR_KEY"></email-builder>

Activate via config:

js
builder.updateConfig({ headless: true });

When headless is active, the shell border and border-radius are also removed so the editor tiles flush into any container.

Wire up your own toolbar:

Every toolbar action is available via the imperative API:

js
// Save
document.querySelector('#save-btn').addEventListener('click', () => builder.save());

// Export HTML
document.querySelector('#export-btn').addEventListener('click', async () => {
  const html = await builder.exportHtml();
  if (html) await sendToESP(html);
});

// Undo / Redo
document.querySelector('#undo-btn').addEventListener('click', () => builder.undo());
document.querySelector('#redo-btn').addEventListener('click', () => builder.redo());

// Preview
document.querySelector('#preview-btn').addEventListener('click', () => builder.togglePreview());

// Template library
document.querySelector('#templates-btn').addEventListener('click', () => builder.openTemplates());

// Viewport toggle
document.querySelector('#mobile-btn').addEventListener('click', () => builder.setViewMode('mobile'));
document.querySelector('#desktop-btn').addEventListener('click', () => builder.setViewMode('desktop'));

// Dirty state indicator
builder.addEventListener('eb:save', () => updateSaveIndicator(false));
builder.addEventListener('eb:change', () => updateSaveIndicator(true));
// or poll:
setInterval(() => updateSaveIndicator(builder.isDirty()), 500);

See Methods for the full API reference.

Plan-gated features ​

Some options are only active when your API key is on a qualifying plan:

OptionRequired plan
accentColorPro+
brandNamePro+
hideBrandingPro+
Template library buttonPro+
Custom fonts (Settings tab)Pro+
Export HTML buttonPro+

TIP

Options outside your plan are silently ignored — no errors, no crashes. This lets you safely pass any config and have features activate automatically when you upgrade.

Locale ​

The editor ships with English (en), Swedish (sv), Norwegian (no), Danish (da), French (fr), and Spanish (es) UI strings. To request an additional locale, contact info@maillune.com.

js
builder.updateConfig({ locale: 'sv' });