Skip to content

Editor props and handle ​

ts
import { EmailEditor, type EmailEditorProps, type EmailEditorHandle } from '@maildun/email-builder/editor';

Props ​

PropTypeDescription
valueEmailDocumentControlled document. Pair with onChange.
defaultValueEmailDocumentInitial document when uncontrolled. Empty when omitted.
onChange(document) => voidEvery committed change; never pending proposals.
readOnlybooleanHides editing chrome.
mergeTags{ key, label? }[]Offered in link fields and the text toolbar.
onUploadImage(file: File) => Promise<{ url, alt? }>Enables Upload on image fields.
onPickImage() => Promise<{ url, alt? } | null>Opens your media library. Enables Choose.
customBlocksCustomBlockDefinition[]Your block types. See Custom blocks.
assetsUrlstringWhere the social icons are hosted. Defaults to jsDelivr.
blockTypesBlockType[]Built-in blocks offered in the palette. All by default.
sectionsSectionName[]Sections in the palette; [] hides them. All by default.
paletteGroupsPaletteGroup[]Your own groups in the palette, e.g. saved sections. See Palette groups.
views('design' | 'preview' | 'code')[]Views in the top bar. All by default.
panels{ sidebar?: boolean; inspector?: boolean }Panels in the default layout. Panels left on can still be hidden from the top bar.
toolbarReactNodeExtra controls at the right of the top bar.
proposalActionsReactNode | (proposal) => ReactNodeExtra controls in the proposal bar, before Reject.
appearance'inherit' | 'light' | 'dark' | 'system'Color scheme. inherit follows a .dark class on an ancestor.
classNamesPartial<Record<EditorSlot, string>>Extra classes for root, topbar, sidebar, stage, canvas, inspector, proposal, block-toolbar.
iconsPartial<Record<EditorIconName, IconSvgElement>>Replacement Hugeicons for proposal (the review bar's mark), reject and accept. Others keep the built-in icons.
messagesDeepPartial<EditorMessages>UI text. See Translations.
onSelectionChange(id, block) => voidThe selected block changed.
onProposalChange(proposal | null) => voidA proposal appeared, changed, or was resolved.
onSave(document) => void⌘S / Ctrl+S inside the editor.
className, styleOn the root element.
refRef<EmailEditorHandle>See below.

EmailEditor.Root takes the same props except panels, plus children. See Custom layouts.

Handle ​

MemberTypeDescription
getDocument() => EmailDocumentThe committed document.
apply(ops: Op | Op[]) => { ok, issues }Applies operations as one undoable step.
undo, redo() => void
propose(ops: Op[], summary?: string) => { ok, issues }Stages changes for review; calls add up into one proposal.
setProposalSummary(summary: string) => voidMessage shown with the pending proposal.
accept, reject() => voidResolves the proposal.
tools(options?) => AgentTool[]Agent tools that add to the proposal. Takes lint and strictSchemas; custom blocks come from the editor.
load(document) => voidOpens another document; clears history, selection and any proposal.
select(id: string | null) => void
render() => RenderResultRenders the committed document. Safe in event handlers.
storeEditorStoreThe underlying store.

Types ​

TypeDescription
Proposal{ document, changed, removed, themeChanged, settingsChanged, ops, label?, summary? }
EditorStateSee Custom layouts.
MergeTag{ key: string; label?: string }
ImageResult{ url: string; alt?: string }
PaletteGroup, PaletteGroupItemHost groups in the palette. See Palette groups.
EditorIcons, EditorIconNameReplacement icons for the icons prop. See Swapping icons.
ApplyOptions{ mergeKey?: string; select?: string | null }, the second argument of store.apply
EditorPanel'sidebar' | 'inspector'
Toast{ id: number; message: string; action?: { label: string; run: () => void } }
EditorView'design' | 'preview' | 'code'
Viewport'desktop' | 'mobile'
EditorSlotThe classNames keys
EditorMessagesEvery UI string; EN_MESSAGES is the English set
DeepPartial, LabelledItem, BlockCategory, PaddingSideHelper types used by EditorMessages

EditorStore ​

The framework-agnostic state behind each editor, with undo/redo (200 steps) and proposals. You can create one yourself for tests or non-React tooling:

ts
import { EditorStore } from '@maildun/email-builder/editor';

const store = new EditorStore(document);
store.apply([{ op: 'remove', id: 'promo' }]);
store.undo();
store.subscribe(() => console.log(store.getState().canRedo));

Methods: getState, subscribe, apply(ops, { mergeKey?, select? }), setDocument, load, undo, redo, select, selectParent, startEditing, stopEditing, setView, setViewport, setPanel, togglePanels, propose(ops, { label?, summary? }), setProposalSummary, accept, reject, showToast, dismissToast, visibleDocument.

Released under the MIT License.