Components
The Vue package provides components for building slide editors with full control over rendering, menus, and custom node views.
Installation
# 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/pmSlideEditor
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.
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)
}<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 menufalse— 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:
<SlideEditor
:content="content"
:editorOptions="{
onCreate: ({ editor }) => console.log('Created'),
onDestroy: () => console.log('Destroyed')
}"
/>Customizing the bubble menu
<SlideEditor
:content="content"
:bubbleMenuPreset="{
items: ['bold', 'italic', 'underline', 'textColor', 'link'],
textColors: ['#000000', '#ff0000', '#00ff00', '#0000ff'],
fonts: ['Inter', 'Georgia', 'Courier New']
}"
/>Disabling the bubble menu
<SlideEditor
:content="content"
:bubbleMenuPreset="false"
/>Configuring editor options
Pass additional editor configuration through editorOptions:
<SlideEditor
:content="content"
:editorOptions="{
autofocus: 'end',
editable: false,
editorProps: {
attributes: {
spellcheck: 'false'
}
}
}"
/>Common options:
autofocus:boolean | 'start' | 'end' | number- Control initial focuseditable:boolean- Toggle edit/read-only modeeditorProps:EditorProps- ProseMirror editor propertiesinjectCSS: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.
import { EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'
const { editor } = useSlideEditor({
content: { type: 'doc', content: [] }
})<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:
<EditorContent
:editor="editor"
class="my-editor"
:style="{ minHeight: '400px' }"
/>.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.
import { BubbleMenuPreset, EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'
const { editor } = useSlideEditor()<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
<BubbleMenuPreset
:editor="editor"
:items="['bold', 'italic', 'underline', 'link', 'textColor']"
/>textColors / highlightColors (string[]) Color palettes for the color pickers. Accepts any valid CSS color.
<BubbleMenuPreset
:editor="editor"
:textColors="['#000000', '#666666', '#ff0000', '#00ff00', '#0000ff']"
:highlightColors="['#ffff00', '#00ffff', '#ff00ff']"
/>fonts (string[]) Font families for the font picker.
<BubbleMenuPreset
:editor="editor"
:fonts="['Inter', 'Georgia', 'Courier New', 'Arial']"
/>fontSizes (string[]) Font size options (any CSS length).
<BubbleMenuPreset
:editor="editor"
:fontSizes="['12px', '16px', '20px', '24px', '32px', '48px']"
/>alignments (('left' | 'center' | 'right' | 'justify')[]) Text alignment options.
<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:
<BubbleMenuPreset
:editor="editor"
:onTextAction="handleTextAction"
/>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 instanceelement— Menu elementtrigger— Button that was clickedgetTriggerRect()/getMenuRect()/getSelectionRect()— Position informationclosePopovers()— Close any open popoversdefaultAction()— Run the built-in action
onImageReplace — Override image replacement behavior:
<BubbleMenuPreset
:editor="editor"
:onImageReplace="handleImageReplace"
/>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 URLreplaceWith(url)— Update image sourceshowDefaultPopover()— Show built-in URL input
Positioning
The bubble menu uses Floating UI for positioning. Configure via the options prop:
<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.
import { BubbleMenu } from '@blockslides/vue-3-prebuilts/menus'
import { EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'
const { editor } = useSlideEditor()<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.
<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.
<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.
import { FloatingMenu } from '@blockslides/vue-3-prebuilts/menus'
import { EditorContent, useSlideEditor } from '@blockslides/vue-3-prebuilts'
const { editor } = useSlideEditor()<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:
<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
import { NodeViewWrapper, NodeViewContent } from '@blockslides/vue-3'
const props = defineProps(['node', 'updateAttributes'])<NodeViewWrapper>
<div :style="{ backgroundColor: node.attrs.bgColor || 'transparent' }">
<NodeViewContent />
</div>
</NodeViewWrapper>Registering the node view
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.
<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.
<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 instancenode— ProseMirror nodedecorations— Decorations arrayselected— Whether node is selectedextension— The extension definitiongetPos()— Get node positionupdateAttributes(attrs)— Update node attributesdeleteNode()— Delete this node
VueNodeViewRenderer options
update — Control when the component re-renders:
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:
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.
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 elementvueInstance— The Vue app instance