Skip to content

Props & events

Props

PropTypeDefaultDescription
v-modelDesignTwo-way design JSON (controlled mode).
blocksBlockDefinition[][]Custom blocks (merged with built-ins).
disabledBlocksstring[][]Hide built-in block types.
themeThemeTokensColor/typography overrides (light + dark).
colorMode'light' | 'dark' | 'auto''light'Theme mode — two-way via v-model:colorMode.
previewbooleanPreview mode — two-way via v-model:preview.
configEditorConfigFeature flags (devices, actions, labels, templates…).
storage'local' | 'none''local'local = localStorage autosave; none = host owns persistence.
onImageUpload(file: File) => Promise<string>base64Upload an image, return its URL.
onSave(design) => void | PromisedownloadHandle Save Design.
onSaveTemplate(payload) => void | PromiseHandle Save as template.
onExport(html, design) => void | PromiseReceive exported HTML.
onLoad() => Design | Promise<Design>Initial design (e.g. server fetch).

Events

EventPayloadFires when
update:modelValuedesignThe design changes (for v-model).
update:colorMode'light' | 'dark'The user toggles the theme (for v-model:colorMode).
update:previewbooleanPreview mode toggles (for v-model:preview).
changedesignThe design changes — debounced (~300ms).
savedesignThe user clicks Save.
save-templatepayloadThe user saves a template.
export(html, design)The user exports HTML.
selectselectionThe current selection changes.
readyapiThe editor is mounted; gives you the imperative API.

Event or prop — not both

The save / save-template / export flows both emit the event and call the matching on* prop. Wire up one — using both @save and :on-save for the same handler runs it twice.

Where the metadata lives

All four header fields save to design.metasubject, from, replyTo, and preview. The preview is also rendered as the email's hidden preheader in the exported HTML.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { EmailEditor } from 'vue-mail-editor'
import type { Design, EditorApi } from 'vue-mail-editor'

const design = ref<Design>()
const editor = ref<EditorApi>()
function onChange(d: Design) {/* persist */}
function send(html: string) {/* send */}
</script>

<template>
  <EmailEditor
    v-model="design"
    @change="onChange"
    @export="(html) => send(html)"
    @ready="(api) => (editor = api)"
  />
</template>
vue
<script setup>
import { ref } from 'vue'
import { EmailEditor } from 'vue-mail-editor'

const design = ref()
const editor = ref()
function onChange(d) {/* persist */}
function send(html) {/* send */}
</script>

<template>
  <EmailEditor
    v-model="design"
    @change="onChange"
    @export="(html) => send(html)"
    @ready="(api) => (editor = api)"
  />
</template>

change vs update:modelValue

update:modelValue fires on every edit (to keep v-model in sync). change is debounced — prefer it for expensive work like writing to a database.

Slots

SlotPurpose
#headerReplace the entire top bar.
#header-brandReplace just the logo / brand area.
#header-actionsInject your own buttons next to the built-in actions.
#emptyCustom empty-canvas state.
#metaCustom email metadata header above the canvas. Scoped: { meta, setMeta } — see Configuration › meta.

See Customizing the top bar for slot examples.

Released under the MIT License.