Skip to content

Vue Overview ​

The @blockslides/vue-3 package provides Vue 3 bindings for Blockslides, wrapping the core editor in Vue-friendly composables and components.

Installation ​

For most slide editor use cases, install the prebuilts package:

bash
pnpm install @blockslides/vue-3-prebuilts @blockslides/core @blockslides/pm @floating-ui/dom

For building custom editors from scratch, install the core Vue package:

bash
pnpm install @blockslides/vue-3 @blockslides/core @blockslides/pm @floating-ui/dom

All packages require Vue 3.0+ as a peer dependency.

Package Structure ​

Blockslides Vue support is split into two packages:

@blockslides/vue-3 - Core primitives for building custom editors

  • Low-level composables (useEditor)
  • Vue renderers and node view components
  • Includes @blockslides/extension-kit and @blockslides/ai-context as dependencies
  • Re-exports all of @blockslides/core

@blockslides/vue-3-prebuilts - Ready-to-use slide editor components

  • SlideEditor - Complete editor component
  • useSlideEditor - Slide editor composable with ExtensionKit
  • Menu components (BubbleMenu, FloatingMenu, BubbleMenuPreset)
  • Re-exports everything from @blockslides/vue-3

Package Dependencies

Unlike the React packages, @blockslides/vue-3 includes @blockslides/extension-kit as a dependency, so ExtensionKit is available even when using the core package directly.

Most users building slide editors should install @blockslides/vue-3-prebuilts. Use @blockslides/vue-3 directly only if building a completely custom editor from scratch.

What's included ​

From @blockslides/vue-3 ​

Core Components:

EditorContent - Renders the ProseMirror editor view and manages node view portals. Required for all editor implementations.

NodeViewWrapper & NodeViewContent - Components for building custom Vue node views.

Core Composables:

useEditor - Lower-level composable for editor instantiation. Use when you need full control over extensions.

Renderers:

VueNodeViewRenderer - Function that creates Vue-based node views for custom block rendering.

VueRenderer - Low-level component renderer used internally by node views.

From @blockslides/vue-3-prebuilts ​

Pre-built Components:

SlideEditor - Drop-in component that bundles editor rendering, viewport scaling, and optional bubble menu preset. Handles instantiation, lifecycle, and common UI patterns.

BubbleMenuPreset - Pre-built bubble menu with formatting controls.

BubbleMenu & FloatingMenu - Positioning components for contextual menus.

Slide Editor Composable:

useSlideEditor - Creates and configures a slide editor with ExtensionKit. Returns editor instance and preset templates.

TIP

Since @blockslides/vue-3-prebuilts re-exports everything from @blockslides/vue-3, you can import any component or composable from either package when using the prebuilts package.

Architecture ​

ExtensionKit integration ​

The useSlideEditor composable automatically configures ExtensionKit, which bundles all extensions needed for slide editing:

  • Text formatting (bold, italic, underline, strike, etc.)
  • Block elements (headings, paragraphs, lists, blockquotes)
  • Slide-specific features (columns, layouts, backgrounds)
  • Media (images, videos, image blocks)
  • Advanced features (tables, math, code blocks)
  • Editor behaviors (undo/redo, drag and drop, markdown parsing)

Extensions can be disabled or configured via extensionKitOptions, and custom extensions can be appended via the extensions prop.

Component vs Composable approach ​

Use SlideEditor for straightforward implementations where the default layout and behavior work for your use case.

Use useSlideEditor + EditorContent when you need:

  • Custom wrapper layouts
  • Multiple editors on the same page
  • Integration with existing state management
  • Custom viewport implementations

Both approaches use the same underlying editor instance and extension system.

Server-side rendering ​

The editor depends on browser APIs and cannot render on the server. In Nuxt.js, wrap editor components in ClientOnly:

html
  <ClientOnly>
    <SlideEditor 
      :content="content"
      :onChange="handleChange"
    />
  </ClientOnly>

Or use lazy loading:

ts
const SlideEditor = defineAsyncComponent(() => 
  import('@blockslides/vue-3-prebuilts').then(m => m.SlideEditor)
)

Custom node views ​

Vue node views let you render custom UI for specific node types using Vue components instead of vanilla DOM manipulation.

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

defineProps(['node', 'updateAttributes'])
html
<NodeViewWrapper>
  <div class="custom-node">
    <NodeViewContent />
  </div>
</NodeViewWrapper>

Register the node view in your extension:

ts
import { Node } from '@blockslides/core'
import { VueNodeViewRenderer } from '@blockslides/vue-3'
import CustomNode from './CustomNode.vue'

const CustomExtension = Node.create({
  name: 'customNode',
  // ... node definition
  addNodeView() {
    return VueNodeViewRenderer(CustomNode)
  }
})

NodeViewWrapper provides drag-and-drop support and marks the element as a node view container. NodeViewContent renders the editable content area for non-leaf nodes.

Re-exports from core ​

The Vue package re-exports the entire @blockslides/core API:

ts
import { 
  Extension,
  Mark,
  Node,
  mergeAttributes,
  getMarkRange,
  // ... all core exports
} from '@blockslides/vue-3'

This means you typically only need to install @blockslides/vue-3 or @blockslides/vue-3-prebuilts - the core exports are available without a separate import.

Reactivity patterns ​

Vue's reactivity system works seamlessly with Blockslides:

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

const { editor } = useSlideEditor()

// Derive reactive state from editor
const isBold = computed(() => editor.value?.isActive('bold'))
const canUndo = computed(() => editor.value?.can().undo())
const wordCount = computed(() => {
  const text = editor.value?.state.doc.textContent || ''
  return text.split(/\s+/).filter(Boolean).length
})
html
<div>
  <button 
    @click="editor?.chain().focus().toggleBold().run()"
    :class="{ active: isBold }"
  >
    Bold
  </button>
  <span>{{ wordCount }} words</span>
</div>

The editor instance is returned as a shallowRef, which means Vue won't deeply watch internal ProseMirror state (for performance). Use computed() to derive reactive values from the editor when needed.