Skip to content

Admin Rich Text Form

Use this recipe for articles, announcements, product descriptions, help-center entries, and similar admin forms. Keep title and summary in your business form, keep the rich text body in the editor v-model, then submit them as one payload.

Try this scenario online

Minimal integration

vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { ProEditorElementPlus } from 'tiptap-vue-pro-element-plus'
import 'element-plus/dist/index.css'
import 'tiptap-vue-pro-element-plus/style.css'

const title = ref('')
const summary = ref('')
const content = ref('<h2>Product update</h2><p>Write the body here.</p>')
const saving = ref(false)

const titleError = computed(() => title.value.trim().length === 0)
const contentError = computed(() => content.value.replace(/<[^>]+>/g, '').trim().length === 0)
const canSubmit = computed(() => !titleError.value && !contentError.value && !saving.value)

async function submitArticle() {
  if (!canSubmit.value) {
    ElMessage.warning('Please enter a title and body')
    return
  }

  saving.value = true
  try {
    await fetch('/api/articles', {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        title: title.value,
        summary: summary.value,
        content: content.value,
      }),
    })
    ElMessage.success('Saved')
  } finally {
    saving.value = false
  }
}
</script>

<template>
  <el-form label-position="top">
    <el-form-item label="Title" :error="titleError ? 'Enter a title' : ''">
      <el-input v-model="title" placeholder="Enter a title" />
    </el-form-item>

    <el-form-item label="Summary">
      <el-input
        v-model="summary"
        type="textarea"
        :rows="3"
        placeholder="Optional list or SEO summary"
      />
    </el-form-item>

    <el-form-item label="Body" :error="contentError ? 'Enter body content' : ''">
      <ProEditorElementPlus
        v-model="content"
        toolbar-layout="compact"
        placeholder="Write the body..."
      />
    </el-form-item>

    <el-button type="primary" :loading="saving" @click="submitArticle">
      Save article
    </el-button>
  </el-form>
</template>

Switching adapters

The editor props are aligned across adapters. Replace only the UI library components and editor entry:

UI libraryEditor componentStyle entry
Element PlusProEditorElementPlustiptap-vue-pro-element-plus/style.css
Naive UIProEditorNaivetiptap-vue-pro-naive/style.css
Ant Design VueProEditorAntDesignVuetiptap-vue-pro-ant-design-vue/style.css

Common pitfalls

  • Do not put title, category, publish status, or other business fields into editor HTML.
  • If your backend stores JSON, pass output="json" to the editor and update your payload type.
  • Do not validate only by HTML string length; strip tags before checking visible text.

A community package built on Tiptap v3 + Vue 3.