Vue rich text editor: keep Tiptap content and saved state separate

In a Vue 3 application, Tiptap supplies an editable rich-text document through @tiptap/vue-3. Read getHTML() when your save path needs HTML, or getJSON() when it stores Tiptap's document representation. Loading content back into the editor is another operation. Keeping those directions explicit helps prevent a reactive update from overwriting what the author is typing.

This example uses the Tiptap 3 documentation and an existing Vue 3 project. It covers a local edit buffer and deliberate replacement; your application still supplies the real save endpoint and its response handling. Vue integration, content representations

Add the editor to the existing Vue project

Install the documented packages, keeping their major versions aligned:

npm install @tiptap/vue-3@3 @tiptap/pm@3 @tiptap/starter-kit@3

The following single-file component adapts those APIs to an original collection notice. It provides an edit area, a bold control, and a button that captures the current HTML into a local output field:

<script setup>
import { ref } from 'vue'
import { EditorContent, useEditor } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'

const capturedHtml = ref('')
const editor = useEditor({
  extensions: [StarterKit],
  content: '<p>Collect your badge at the side desk.</p>',
})

function capture() {
  if (!editor.value) return
  capturedHtml.value = editor.value.getHTML()
}
</script>

<template>
  <section aria-label="Collection notice editor">
    <button
      type="button"
      :disabled="!editor"
      @click="editor?.chain().focus().toggleBold().run()"
    >Bold</button>
    <EditorContent :editor="editor" />
    <button type="button" :disabled="!editor" @click="capture">
      Capture current HTML
    </button>
    <h2>Local HTML capture</h2>
    <pre>{{ capturedHtml }}</pre>
  </section>
</template>

<style scoped>
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>

EditorContent renders the editing surface. The pre displays the captured string as text through Vue interpolation, so an author can inspect tags. It does not render a second editable copy of the notice. Add the application's intended editor styling; Tiptap is a headless editor rather than a finished toolbar design.

This is an integration fragment to run in your Vue application. The capture button takes a local snapshot. Its label deliberately does not claim a server save.

Choose a storage representation before wiring Save

For a CMS that stores HTML fragments, capture editor.value.getHTML() inside the real save handler and send that value through the application's existing persistence route. For a Tiptap document store, retrieve getJSON() instead. The output guide documents both representations and restoring them as editor content.

Choose one authoritative stored representation and keep the relevant extension configuration with it. A JSON object from Tiptap is a structured editor document; it is different from an arbitrary object extracted from an HTML page.

After a successful save response, reopen that record and inspect its words and supported formatting. A response that only echoes the submitted payload is not proof that a later fetch will return it.

Replace loaded content without creating an update loop

When an API response intentionally replaces the current notice, the Tiptap 3 command accepts an options object:

editor.value.commands.setContent(loadedHtml, {
  emitUpdate: false
})

Run that only after the editor exists and after deciding that replacement is appropriate. For example, switching from one record to another should first resolve any unsaved changes to the current record.

Tiptap 3 defaults emitUpdate to true, a change from version 2. Suppressing that update event can prevent a load operation from reentering a listener that handles user changes. The command also renders only content permitted by the configured schema. setContent options

Avoid a broad watcher that calls setContent every time the same document's state changes. The editor already owns a live editing state. Continually replacing that document can disturb the author's work and obscure which change should be saved.

Check the actual authoring sequence

With the collection fixture, select “side desk,” apply Bold, and capture the current HTML. Confirm the words remain intact and the selected phrase has emphasis. Change the desk instruction and capture again; the second output should contain the new wording.

Then exercise the real save and reopen flow. If the stored words are right but the layout differs, check the reader stylesheet. If content disappears while loading, check the enabled schema and extensions. Vue in a standalone HTML page covers a separate approach; the .vue component above belongs in the project's build process.

Treat user-authored rich text as untrusted input at the application's display boundary. Define the permitted content and enforce the application's rendering policy before showing stored HTML to other readers. Finish by reopening the saved notice in its actual reader view, where the collection instruction must remain clear after the editing session ends.

Keep reading