MailCraft
Drag-and-drop email editor as a Web Component. No dependencies, no framework, no build step.
npm install @seliseblocks/mailcraft
Use
import '@seliseblocks/mailcraft'; // registers <mailcraft-editor>
<mailcraft-editor id="editor"></mailcraft-editor>
Or mount it into a container from code, without writing the tag:
import { createEditor } from '@seliseblocks/mailcraft';
const editor = createEditor('#mail', { html, toolbar: false });
editor.exportHtml();
editor.destroy();
That is a working editor. It speaks HTML in both directions — there is no document format to store or migrate:
editor.loadTemplate({ name: 'Welcome', html }); // give it HTML
const html = editor.exportHtml(); // get send-ready HTML back
Exported HTML is valid input to loadTemplate, so saving the export is saving the user’s work.
Configure
variables="first_name,company" |
merge tags, exported as `` |
locale="de" |
31 languages, RTL automatic |
theme="light" \| "dark" |
host owns light/dark; hides the built-in toggle |
ui-font="inherit" |
match your app’s font |
accent="#e11d48" |
your brand color, everywhere the editor is accented; also var(--brand) or inherit |
toolbar="none" |
hide the editor’s own top bar |
footer="none" |
drop the “Powered by SELISE Blocks © 2026” strip, or pass your own line |
toolbar="undo,redo,export" |
…or keep only these parts |
.storageProvider + .storageLimits |
image uploads to your backend |
.aiProvider |
async (prompt) => text, powers the AI draft panel |
.messages |
override any UI string |
Every attribute is also a property. Full reference and integration recipes: DOCS.md.
For AI agents
Everything an assistant needs to wire this up correctly:
PACKAGE @seliseblocks/mailcraft — Web Component, zero runtime deps
IMPORT import '@seliseblocks/mailcraft' (side effect: registers the element)
Safe to import under SSR; the element itself renders in a browser only.
ELEMENT <mailcraft-editor id="editor"></mailcraft-editor>
MOUNT createEditor(target, options) -> handle (no tag needed)
target = CSS selector or Element; throws if it matches nothing
options = html, name, variables, locale, dir, theme, uiFont, accent, toolbar,
footer, storageProvider, storageLimits, aiProvider, iconProvider,
messages, height, replace, onChange(doc), onExport(html)
name = template name for `html`; read only alongside it
handle = the METHODS below + .element + .destroy()
The container supplies the height; the editor fills it.
DATA HTML in, HTML out. There is no JSON document format in the public API.
editor.loadTemplate({ name, html }) apply HTML (undoable, deep-copied)
editor.importHtml(html) apply HTML (same importer)
editor.exportHtml() -> string send-ready email HTML
Persist by storing exportHtml() and passing it back to loadTemplate().
Unclassifiable markup survives as a raw-HTML block. Nothing is dropped.
EVENTS 'change' detail = internal doc (do not persist this)
'export' detail = HTML string
ATTRS variables="a,b,c" | locale="de" | theme="dark" | dir="rtl"
ui-font="inherit" | accent="#e11d48" | accent="var(--brand)"
toolbar="none" | toolbar="undo,redo,export" | footer="none"
PROPS .variables .toolbar .footer .uiFont .accent .messages .aiProvider .iconProvider
.storageProvider .storageLimits
METHODS exportHtml() importHtml(html) loadTemplate(tpl) undo() redo()
screenshotPng() previewScreenshot() downloadScreenshot() copyScreenshot()
KEYS Bound on window, so they survive toolbar="none":
Esc (leave field / deselect + close), Ctrl/Cmd+Z, Shift+Ctrl/Cmd+Z,
Ctrl/Cmd+E (export dialog), Ctrl/Cmd+K (link, while editing),
Ctrl/Cmd+D (duplicate), Backspace|Delete (delete selection).
UPLOADS Limits and a provider are both required, or every upload is refused.
editor.storageLimits = { accept:['image/jpeg','image/png','image/gif'],
maxBytes: 2*1024*1024 }
editor.storageProvider = { list(q), upload(file, o), folders?(), remove?(a),
limits? }
provider.limits is merged per key under .storageLimits, and satisfies
the requirement on its own. .storageProvider = null restores the demo library.
The editor never makes a network request itself.
TOOLBAR Parts: logo status device undo redo theme ai code preview export
Attribute = allow-list (keep these). Property = { part: false } (drop these).
none|hidden|off|false = no bar at all; all = the default.
Hidden undo/redo/export stay reachable — methods, plus Ctrl/Cmd+Z, +E.
preview, code and ai are bar-only: hiding them removes the panel.
DO NOT Do not use getContent()/setContent() — internal, shape may change.
Do not expect a campaign/title option — there is none; <title> is "Email".
Develop
npm install && node build.js && npm test
examples/vanilla.html is a complete host page — open it directly, no server needed. It ships in the npm package too, so it is there after an install; the hosted copy is the same file.
MIT