Skip to content

Plugins

The plugin system lets you extend the editor with custom blocks, categories, toolbar actions, sidebar panels, and property editors — without modifying the source.

Creating a Plugin

A plugin is a function that receives a PluginContext:

ts
import type { Plugin } from '@lab2view/vue-email-editor'

const myPlugin: Plugin = (ctx) => {
  // Use ctx to register extensions
}

Register It

vue
<EmailEditor :plugins="[myPlugin]" />

Plugins are initialized before the editor:ready event fires.

Adding Custom Blocks

ts
import type { Plugin } from '@lab2view/vue-email-editor'
import { createText, createImage, createSection, createColumn } from '@lab2view/vue-email-editor'

const brandPlugin: Plugin = (ctx) => {
  // Simple block
  ctx.registerBlock({
    id: 'brand-signature',
    label: 'Signature',
    category: 'content',
    icon: 'PenTool',
    factory: () => createText(`
      <p style="font-size: 14px; color: #666;">
        <strong>John Doe</strong><br/>
        Product Manager at Acme Corp<br/>
        <a href="mailto:john@acme.com">john@acme.com</a>
      </p>
    `),
  })

  // Complex block (section with multiple columns)
  ctx.registerBlock({
    id: 'brand-header',
    label: 'Brand Header',
    category: 'composite',
    icon: 'Layout',
    factory: () => {
      const logo = createImage({
        src: 'https://acme.com/logo.png',
        alt: 'Acme Corp',
        width: '150px',
      })
      const col = createColumn([logo])
      return createSection([col], {
        'background-color': '#1a1a2e',
        padding: '20px 0',
      })
    },
  })
}

Adding Block Categories

ts
const myPlugin: Plugin = (ctx) => {
  ctx.registerBlockCategory({
    id: 'brand',
    label: 'Brand',
    icon: 'Building',
    order: 5, // Lower = displayed first
  })

  ctx.registerBlock({
    id: 'brand-banner',
    label: 'Brand Banner',
    category: 'brand', // Uses our new category
    icon: 'Flag',
    factory: () => createText('<h1>Welcome to Acme</h1>'),
  })
}

Adding Toolbar Actions

ts
const savePlugin: Plugin = (ctx) => {
  ctx.registerToolbarAction({
    id: 'save',
    label: 'Save',
    icon: 'Save',
    position: 'right',
    order: 10,
    handler: () => {
      console.log('Saving...')
    },
  })

  ctx.registerToolbarAction({
    id: 'preview',
    label: 'Preview',
    icon: 'Eye',
    position: 'right',
    order: 20,
    handler: () => {
      console.log('Opening preview...')
    },
  })
}

Listening to Events

Plugins receive the full event system:

ts
const analyticsPlugin: Plugin = (ctx) => {
  ctx.on('editor:change', ({ document }) => {
    // Track changes
    analytics.track('email_edited', {
      nodeCount: countNodes(document.body),
    })
  })

  ctx.on('node:deleted', ({ nodeId }) => {
    console.log(`Node ${nodeId} deleted`)
  })

  ctx.on('block:dropped', ({ blockId, parentId }) => {
    console.log(`Block ${blockId} dropped into ${parentId}`)
  })
}

Async Plugins

Plugins can be async (e.g., to fetch remote block definitions):

ts
const remoteBlocksPlugin: Plugin = async (ctx) => {
  const response = await fetch('/api/custom-blocks')
  const blocks = await response.json()

  for (const block of blocks) {
    ctx.registerBlock({
      id: block.id,
      label: block.name,
      category: 'custom',
      icon: block.icon || 'Box',
      factory: () => createText(block.defaultHtml),
    })
  }
}

Plugin Context API

MethodDescription
registerBlock(block)Add a block to the blocks panel
registerBlockCategory(category)Add a new category tab
registerPropertyEditor(type, component)Override a property editor for a type
registerToolbarAction(action)Add a button to the toolbar
registerSidebarPanel(panel)Add a sidebar tab
on(event, handler)Listen to editor events
off(event, handler)Remove an event listener
labelsReactive reference to current EditorLabels

Multiple Plugins

vue
<EmailEditor :plugins="[brandPlugin, analyticsPlugin, savePlugin]" />

Plugins are executed in order. Duplicate block/category IDs are silently ignored (first registration wins).

Released under the MIT License.