Skip to content

Components ​

The Vue package provides components for building slide editors with full control over rendering, menus, and custom node views.

Installation ​

bash
# For pre-built components (includes everything from @blockslides/vue-3)
pnpm install @blockslides/vue-3-prebuilts @blockslides/core @blockslides/pm 

# Or for core Vue bindings only
pnpm install @blockslides/vue-3 @blockslides/core @blockslides/pm

SlideEditor ​

SlideEditor is a complete editor component that combines the editor content with a built-in bubble menu preset. Use this when you want a fully-featured editor with minimal setup.

ts
import { SlideEditor } from '@blockslides/vue-3-prebuilts'
import { ref } from 'vue'

const content = ref({
  type: 'doc',
  content: []
})

const handleChange = (doc) => {
  console.log('Content changed:', doc)
}
html
<SlideEditor 
  :content="content"
  :onChange="handleChange"
  :editorOptions="{ autofocus: true, editable: true }"
/>

Props ​

content (JSONContent) Initial document content.

extensions (AnyExtension[]) Additional extensions to append after ExtensionKit.

extensionKitOptions (ExtensionKitOptions) Customize or disable ExtensionKit features.

bubbleMenuPreset (boolean | BubbleMenuPresetProps)

  • true (default) — Renders the default bubble menu
  • false — Disables the bubble menu entirely
  • Object — Pass configuration to customize the bubble menu

presetTemplates (PresetTemplates) Custom template list for the add-slide button.

theme ('light' | 'dark') Theme name applied as CSS class.

editorProps (EditorProps) ProseMirror EditorProps for advanced configuration.

editorOptions (Partial<EditorOptions>) Additional editor options to pass through to the core editor. Use this to pass options like autofocus, editable, etc.

class / style Applied to the outer wrapper element.

Events / Callbacks ​

onChange ((doc: JSONContent, editor: Editor) => void) Called on every document update with the current JSON. Pass as a prop (:onChange).

onUpdate (({ editor, transaction }) => void) Called on every editor transaction. Pass as a prop (:onUpdate).

onEditorReady ((editor: Editor) => void) Called once when the editor instance is created. Pass as a prop (:onEditorReady).

Other lifecycle hooks Pass onCreate, onBeforeCreate, onDestroy, onFocus, onBlur, onSelectionUpdate, onTransaction via the :editorOptions prop:

html
<SlideEditor
  :content="content"
  :editorOptions="{
    onCreate: ({ editor }) => console.log('Created'),
    onDestroy: () => console.log('Destroyed')
  }"
/>

Customizing the bubble menu ​

html
<SlideEditor
  :content="content"
  :bubbleMenuPreset="{
    items: ['bold', 'italic', 'underline', 'textColor', 'link'],
    textColors: ['#000000', '#ff0000', '#00ff00', '#0000ff'],
    fonts: ['Inter', 'Georgia', 'Courier New']
  }"
/>

Disabling the bubble menu ​

html
<SlideEditor
  :content="content"
  :bubbleMenuPreset="false"
/>

Configuring editor options ​

Pass additional editor configuration through editorOptions:

html
<SlideEditor
  :content="content"
  :editorOptions="{
    autofocus: 'end',
    editable: false,
    editorProps: {
      attributes: {
        spellcheck: 'false'
      }
    }
  }"
/>

Common options:

  • autofocus: boolean | 'start' | 'end' | number - Control initial focus
  • editable: boolean - Toggle edit/read-only mode
  • editorProps: EditorProps - ProseMirror editor properties
  • injectCSS: boolean - Control CSS injection

EditorContent ​

EditorContent renders the ProseMirror editor view. Use this for complete control over your editor setup, typically combined with useSlideEditor or useEditor.

ts
import { EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'

const { editor } = useSlideEditor({
  content: { type: 'doc', content: [] }
})
html
<EditorContent 
  v-if="editor" 
  :editor="editor" 
  class="my-editor"
/>

Props ​

editor (Editor | null, required) The editor instance to render.

class (string) CSS classes applied to the wrapper element.

style (StyleValue) Inline styles applied to the wrapper element.

Styling the editor ​

The component renders a div containing the ProseMirror editor. Apply styles directly:

html
<EditorContent 
  :editor="editor"
  class="my-editor"
  :style="{ minHeight: '400px' }"
/>
css
.my-editor {
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
}

BubbleMenuPreset ​

A fully-featured bubble menu with text formatting controls (bold, italic, underline, colors, fonts, alignment) and image editing tools (replace, align, crop, dimensions). Appears when text is selected or an image is clicked.

ts
import { BubbleMenuPreset, EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'

const { editor } = useSlideEditor()
html
<div v-if="editor">
  <EditorContent :editor="editor" />
  <BubbleMenuPreset :editor="editor" />
</div>

Props ​

editor (Editor, required) The editor instance.

items (BubbleMenuPresetItem[]) Controls and their display order. Available items:

  • 'undo' / 'redo' — History controls
  • 'fontFamily' — Font picker dropdown
  • 'fontSize' — Font size dropdown
  • 'bold' / 'italic' / 'underline' — Text formatting
  • 'textColor' — Text color picker
  • 'highlightColor' — Background highlight picker
  • 'link' — Link editor
  • 'align' — Text alignment dropdown
html
  <BubbleMenuPreset
    :editor="editor"
    :items="['bold', 'italic', 'underline', 'link', 'textColor']"
  />

textColors / highlightColors (string[]) Color palettes for the color pickers. Accepts any valid CSS color.

html
  <BubbleMenuPreset
    :editor="editor"
    :textColors="['#000000', '#666666', '#ff0000', '#00ff00', '#0000ff']"
    :highlightColors="['#ffff00', '#00ffff', '#ff00ff']"
  />

fonts (string[]) Font families for the font picker.

html
  <BubbleMenuPreset
    :editor="editor"
    :fonts="['Inter', 'Georgia', 'Courier New', 'Arial']"
  />

fontSizes (string[]) Font size options (any CSS length).

html
  <BubbleMenuPreset
    :editor="editor"
    :fontSizes="['12px', '16px', '20px', '24px', '32px', '48px']"
  />

alignments (('left' | 'center' | 'right' | 'justify')[]) Text alignment options.

html
  <BubbleMenuPreset
    :editor="editor"
    :alignments="['left', 'center', 'right']"
  />

injectStyles (boolean, default: true) Automatically inject default styles. Set to false if providing your own styles.

class (string) Additional CSS classes for the menu element.

Advanced: Intercepting actions ​

onTextAction — Override default behavior for text formatting buttons:

html
<BubbleMenuPreset
  :editor="editor"
  :onTextAction="handleTextAction"
/>
ts
const handleTextAction = (action, ctx) => {
  if (action === 'bold') {
    // Custom bold logic
    console.log('Bold clicked')
    ctx.defaultAction() // Call default if needed
  } else {
    ctx.defaultAction()
  }
}

The context object provides:

  • editor — Editor instance
  • element — Menu element
  • trigger — Button that was clicked
  • getTriggerRect() / getMenuRect() / getSelectionRect() — Position information
  • closePopovers() — Close any open popovers
  • defaultAction() — Run the built-in action

onImageReplace — Override image replacement behavior:

html
<BubbleMenuPreset
  :editor="editor"
  :onImageReplace="handleImageReplace"
/>
ts
const handleImageReplace = (ctx) => {
  // Open your custom image picker
  openImagePicker((url) => {
    ctx.replaceWith(url)
  })
  
  // Or use the built-in popover
  // ctx.showDefaultPopover()
}

Additional context methods:

  • getCurrentValue() — Get current image URL
  • replaceWith(url) — Update image source
  • showDefaultPopover() — Show built-in URL input

Positioning ​

The bubble menu uses Floating UI for positioning. Configure via the options prop:

html
  <BubbleMenuPreset
    :editor="editor"
    :options="{
      placement: 'top',
      offset: 12,
      flip: {},
      shift: { padding: 8 }
    }"
  />

BubbleMenu ​

A lower-level bubble menu component for building custom menus. Provides positioning and visibility logic without built-in controls.

ts
import { BubbleMenu } from '@blockslides/vue-3-prebuilts/menus'
import { EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'

const { editor } = useSlideEditor()
html
<div v-if="editor">
  <EditorContent :editor="editor" />
  <BubbleMenu :editor="editor">
    <button @click="editor.chain().focus().toggleBold().run()">
      Bold
    </button>
    <button @click="editor.chain().focus().toggleItalic().run()">
      Italic
    </button>
  </BubbleMenu>
</div>

Props ​

editor (Editor, required) Editor instance.

pluginKey (string, default: 'bubbleMenu') Unique identifier if using multiple bubble menus.

updateDelay (number, default: 250) Debounce delay for position updates (in milliseconds).

resizeDelay (number, default: 60) Throttle delay for resize events (in milliseconds).

shouldShow (({ editor, state, view, from, to }) => boolean) Controls menu visibility. By default, shows when text is selected.

html
  <BubbleMenu
    :editor="editor"
    :shouldShow="({ editor }) => editor.isActive('heading')"
  >
    <HeadingControls />
  </BubbleMenu>

appendTo (HTMLElement | (() => HTMLElement)) Container element for the menu. Defaults to document.body.

options (Partial<ComputePositionConfig>) Floating UI positioning options.

html
  <BubbleMenu
    :editor="editor"
    :options="{
      placement: 'bottom',
      offset: 8,
      flip: { fallbackPlacements: ['top', 'left', 'right'] }
    }"
  >
    <slot />
  </BubbleMenu>

FloatingMenu ​

A menu that appears on empty lines, useful for inserting new content blocks.

ts
import { FloatingMenu } from '@blockslides/vue-3-prebuilts/menus'
import { EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'

const { editor } = useSlideEditor()
html
<div v-if="editor">
  <EditorContent :editor="editor" />
  <FloatingMenu :editor="editor">
    <button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()">
      H1
    </button>
    <button @click="editor.chain().focus().toggleBulletList().run()">
      List
    </button>
  </FloatingMenu>
</div>

Props ​

Same as BubbleMenu, except shouldShow defaults to showing on empty lines.

shouldShow — Override default empty-line detection:

html
  <FloatingMenu
    :editor="editor"
    :shouldShow="({ editor, state }) => {
      const { $anchor } = state.selection
      const node = $anchor.parent
      // Only show in paragraphs
      return node.type.name === 'paragraph' && node.content.size === 0
    }"
  >
    <slot />
  </FloatingMenu>

Custom Node Views ​

Build custom Vue components for specific node types using VueNodeViewRenderer, NodeViewWrapper, and NodeViewContent.

Creating a custom node view ​

ts
import { NodeViewWrapper, NodeViewContent } from '@blockslides/vue-3'

const props = defineProps(['node', 'updateAttributes'])
html
<NodeViewWrapper>
  <div :style="{ backgroundColor: node.attrs.bgColor || 'transparent' }">
    <NodeViewContent />
  </div>
</NodeViewWrapper>

Registering the node view ​

ts
import { Paragraph } from '@blockslides/extension-paragraph'
import { VueNodeViewRenderer } from '@blockslides/vue-3'
import CustomParagraph from './CustomParagraph.vue'

const CustomParagraphExtension = Paragraph.extend({
  addNodeView() {
    return VueNodeViewRenderer(CustomParagraph)
  },
  
  addAttributes() {
    return {
      ...this.parent?.(),
      bgColor: {
        default: null,
        parseHTML: element => element.getAttribute('data-bg-color'),
        renderHTML: attributes => ({
          'data-bg-color': attributes.bgColor
        })
      }
    }
  }
})

NodeViewWrapper ​

Required wrapper for all custom node views. Handles drag behavior and proper DOM structure.

as (string, default: 'div') HTML element to render.

html
  <NodeViewWrapper as="section" class="my-node">
    <slot />
  </NodeViewWrapper>

NodeViewContent ​

Marks where editable content should render. Required for nodes that have content.

as (string, default: 'div') HTML element to render.

html
  <NodeViewContent as="span" />

TIP

For leaf nodes (like images or horizontal rules), omit NodeViewContent entirely.

Node View Props ​

All custom node view components receive these props:

  • editor — Editor instance
  • node — ProseMirror node
  • decorations — Decorations array
  • selected — Whether node is selected
  • extension — The extension definition
  • getPos() — Get node position
  • updateAttributes(attrs) — Update node attributes
  • deleteNode() — Delete this node

VueNodeViewRenderer options ​

update — Control when the component re-renders:

ts
VueNodeViewRenderer(CustomNode, {
  update({ oldNode, newNode, updateProps }) {
    // Only re-render if specific attributes changed
    if (oldNode.attrs.src !== newNode.attrs.src) {
      updateProps()
      return true
    }
    // Don't re-render, node hasn't changed meaningfully
    return true
  }
})

className — Customize the wrapper element:

ts
VueNodeViewRenderer(CustomNode, {
  className: 'custom-node-wrapper'
})

VueRenderer ​

Low-level class for rendering Vue components anywhere in your editor. Useful for tooltips, popovers, or other UI elements that need to interact with the editor.

ts
import { VueRenderer } from '@blockslides/vue-3'
import MyTooltip from './MyTooltip.vue'

// In your extension or plugin
const renderer = new VueRenderer(MyTooltip, {
  editor,
  props: { text: 'Hello' }
})

// Append to DOM
document.body.appendChild(renderer.element)

// Update props
renderer.updateProps({ text: 'Updated' })

// Cleanup
renderer.destroy()

Constructor options ​

editor (Editor, required) The editor instance.

props (Record<string, any>) Props passed to the component.

Methods ​

updateProps(props) — Update component props and trigger re-render.

destroy() — Unmount component and cleanup.

Properties ​

  • element — The wrapper DOM element
  • vueInstance — The Vue app instance