useSaEditor
A Tiptap editor with the template's extension set already configured — headings, lists, links, images, tables and highlighted code blocks.
Import
ts
import { useSaEditor } from '@core/editor/useEditor'The file is useEditor.ts, but the export is useSaEditor — Tiptap's own useEditor is what it wraps.
Signature
ts
function useSaEditor(options?: SaEditorOptions): ShallowRef<Editor | undefined>
interface SaEditorOptions {
modelValue?: string
placeholder?: string
readonly?: boolean
onUpdate?: (html: string) => void
}Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
modelValue | string | '' | Starting HTML. |
placeholder | string | common.startTyping | Placeholder text, and the region's accessible name. |
readonly | boolean | false | Renders the content without letting it be edited. |
onUpdate | (html: string) => void | — | Called on every change with editor.getHTML(). |
Returns
A ShallowRef<Editor | undefined>. It is undefined until the editor mounts, so guard it or use v-if.
The extensions
| Extension | What it adds |
|---|---|
| StarterKit | Headings, paragraphs, bold, italic, lists, quotes, history, and Link with openOnClick: false. Its own codeBlock is switched off. |
| Image | Inline images. |
| Placeholder | The empty-state text. |
| Table, TableRow, TableHeader, TableCell | Resizable tables. |
| CodeBlockLowlight | Code blocks highlighted by lowlight's common language set. |
Example
vue
<script setup lang="ts">
import { useSaEditor } from '@core/editor/useEditor'
import { EditorContent } from '@tiptap/vue-3'
import { ref } from 'vue'
const html = ref('<p>Start with a sentence.</p>')
const editor = useSaEditor({
modelValue: html.value,
placeholder: 'Write the release note',
onUpdate: value => (html.value = value),
})
</script>
<template>
<div class="space-y-3">
<div v-if="editor" class="flex gap-2">
<button type="button" @click="editor.chain().focus().toggleBold().run()">
Bold
</button>
<button type="button" @click="editor.chain().focus().toggleBulletList().run()">
List
</button>
</div>
<EditorContent :editor="editor" class="rounded-lg border p-3" />
</div>
</template>Notes
- Link, Underline and a plain code block already ship inside StarterKit in Tiptap v3. Adding
@tiptap/extension-linkagain triggers the duplicate-extension warning, so Link is configured through StarterKit and the built-incodeBlockis disabled in favor of CodeBlockLowlight. - The lowlight registry is created once at module scope and shared by every editor on the page.
- The editable region gets an
aria-label. Tiptap setsrole="textbox"andaria-multiline, but a contenteditable div is not a form element, so no<label for>can name it. The placeholder, or the defaultcommon.richTextEditor, fills that gap. readonlymaps to Tiptap'seditable: false. The content still renders.- The default placeholder and label are translation keys, so they follow the language switcher.
SaEditor.vuewraps this with a toolbar. Reach for the composable when you want a different toolbar, not a different editor.