Vue 3

    Vue Rich Text Editor

    Scribe Editor integrates seamlessly with Vue 3's Composition API. Under 50KB, zero dependencies, v-model support, floating toolbar, and a clean useScribe composable. Works with Nuxt 3 and Vite out of the box.

    $npm install scribejs-editor
    < 50KB
    Gzipped bundle
    0
    Runtime dependencies
    v-model
    Native support

    Built for the Vue 3 Composition API

    Composition API native

    Built for Vue 3 <script setup> with onMounted / onBeforeUnmount lifecycle. A clean composable is all you need.

    v-model compatible

    Emit update:modelValue from onChange. Bind with v-model="content" and it just works — no wrappers.

    Works with Nuxt 3

    Wrap in <ClientOnly> to skip SSR. Scribe hydrates cleanly with no hydration mismatches or window errors.

    Zero dependencies

    Under 50KB gzipped, no ProseMirror, no Quill. Your Vue bundle stays as small as possible.

    Vue 3 Component with v-model

    A complete, reusable Vue SFC with defineExpose for imperative access and v-model for reactive binding.

    <!-- ScribeEditor.vue -->
    <script setup lang="ts">
    import { ref, onMounted, onBeforeUnmount } from 'vue'
    import { Scribe, type EditorInstance } from 'scribejs-editor'
    import 'scribejs-editor/dist/scribe.css'
    
    const props = defineProps<{ modelValue?: string }>()
    const emit = defineEmits<{ 'update:modelValue': [string] }>()
    
    const editorEl = ref<HTMLElement | null>(null)
    let editor: EditorInstance | null = null
    
    onMounted(() => {
      if (!editorEl.value) return
      editor = Scribe.init(editorEl.value, {
        placeholder: 'Start writing...',
        onChange: (html) => emit('update:modelValue', html),
      })
    })
    
    onBeforeUnmount(() => editor?.destroy())
    
    // Expose API to parent via template ref
    defineExpose({
      bold:    () => editor?.bold(),
      italic:  () => editor?.italic(),
      getHTML: () => editor?.getHTML() ?? '',
    })
    </script>
    
    <template>
      <div ref="editorEl" v-html="modelValue" />
    </template>

    Composable Pattern

    Extract editor logic into a reusable useScribe composable and share it across components.

    // composables/useScribe.ts
    import { ref, onMounted, onBeforeUnmount, type Ref } from 'vue'
    import { Scribe, type EditorInstance } from 'scribejs-editor'
    
    export function useScribe(targetRef: Ref<HTMLElement | null>) {
      const editor = ref<EditorInstance | null>(null)
    
      onMounted(() => {
        if (!targetRef.value) return
        editor.value = Scribe.init(targetRef.value, {
          toolbar: 'floating',
        })
      })
    
      onBeforeUnmount(() => editor.value?.destroy())
    
      return {
        bold:    () => editor.value?.bold(),
        italic:  () => editor.value?.italic(),
        heading: (n: 1|2|3|4|5|6) => editor.value?.heading(n),
        link:    (url: string) => editor.value?.link(url),
        getHTML: () => editor.value?.getHTML() ?? '',
      }
    }
    
    // Usage in any component:
    // const editorEl = ref(null)
    // const { bold, italic, getHTML } = useScribe(editorEl)

    Nuxt 3 Integration

    Use Nuxt's built-in <ClientOnly> to skip SSR for DOM-bound editors.

    <!-- Nuxt 3 — wrap in <ClientOnly> to skip SSR -->
    <template>
      <ClientOnly>
        <ScribeEditor v-model="content" />
      </ClientOnly>
    </template>
    
    <script setup>
    const content = ref('<p>Hello Nuxt!</p>')
    </script>

    Full feature set — ready to use

    Bold, italic, underline, strikethrough
    Headings H1–H6
    Ordered & unordered lists
    Links with inline editing
    Floating toolbar on selection
    Fixed toolbar mode
    Paste safe from Word & Google Docs
    Built-in XSS sanitization
    Iframe editing support
    Plugin system for extensibility
    Full TypeScript types included
    MIT license — free forever

    Ship a Vue rich text editor today.

    One npm install. One composable. A complete WYSIWYG editor in your Vue 3 app in minutes.

    Vue 3 + Scribe — common questions

    What is the best rich text editor for Vue 3?

    Scribe Editor is one of the best lightweight WYSIWYG editors for Vue 3. It's under 50KB with zero dependencies, integrates via Composition API (onMounted / onBeforeUnmount), supports v-model with update:modelValue, and includes a floating toolbar. It works with both Vite and Nuxt 3.

    How do I add a rich text editor to a Vue 3 app?

    Install scribejs-editor, create a Vue component with a ref to the editor element, call Scribe.init(el, options) in onMounted, and clean up with editor.destroy() in onBeforeUnmount. Emit update:modelValue in the onChange callback for v-model support.

    Does Scribe Editor work with Nuxt 3?

    Yes. Wrap your Scribe-powered component in Nuxt's <ClientOnly> tag to prevent server-side rendering. The editor will initialize on the client after hydration with no window or document errors.

    Is Scribe a good alternative to Tiptap for Vue?

    Yes, especially if you don't need Tiptap's ProseMirror-based custom schemas. Scribe is 3x smaller (50KB vs 150KB+), doesn't require you to configure extensions, and has first-class Vue 3 support — unlike Tiptap which is primarily React-focused. Scribe's Vue composable pattern fits naturally with the Composition API.