将 Tiptap 集成到 Svelte 中

完全指南

由于官方 Tiptap 文档缺乏相关信息,我在将这个流行的文本编辑器集成到 Svelte 项目时遇到了困难。本教程将补充缺失的内容,并提供一份全面的指南,帮助你在 Svelte 中成功运行 Tiptap。

DAVID YANG

发布于 Aug 6, 2025 • 4 分钟阅读

Cover Image

介绍

在构建我的 Svelte 项目时,我需要一个功能强大、可扩展的富文本编辑器,能够支持自定义以及现代化的用户体验特性。经过对现有编辑器的研究,我选择了 Tiptap,因为它具有灵活性、活跃的社区开发,以及基于 ProseMirror 的现代架构。

然而,将 Tiptap 集成到 Svelte 环境中并不像我预想的那样简单。虽然官方文档对 React 和 Vue 的支持非常全面,但对于 Svelte 用户几乎没有任何指导。
本教程旨在填补这一空白,帮助其他人顺利地在 Svelte 项目中运行 Tiptap。


为什么现有的 Tiptap 文档不够用

我遇到的几个痛点:

  • 没有如何在编辑器内外传递数据的示例
    官方指南没有涉及如何在 Tiptap 内部状态和 Svelte 的响应式系统之间进行绑定。

  • 添加控制按钮时出现错误
    当我尝试添加自定义按钮来控制格式时,会遇到一些意外错误,这与 Tiptap 处理命令和编辑器状态的方式有关。

  • 缺少控制面板样式的指导
    几乎没有关于如何美化控制按钮和面板,或者如何在 UI 中反映当前格式状态的帮助。

本教程将解决以上所有问题。


安装与初始化

按照官方的 Tiptap 指南进行安装:

Tiptap Svelte 安装指南


让数据流动起来

下面介绍如何以响应式的方式在 Tiptap 中传入数据并获取更新。

示例:双向绑定

以下是 Tiptap.svelte 的示例代码,用于绑定编辑器的数据:

<script lang="ts">
  import "./Tiptap-styles.scss";
  import StarterKit from "@tiptap/starter-kit";
  import { Editor } from "@tiptap/core";
  import { onMount, onDestroy } from "svelte";
  import { TableKit } from '@tiptap/extension-table'

  /** 输入/输出 */
  export let html: string;
  /** 仅输出 */
  export let editor: Editor;

  let rootEl: HTMLDivElement;
  let lastHTML: string | null = null;

  onMount(onLoad);

  function onLoad() {
    createEditor();
  }

  function createEditor() {
    editor = new Editor({
      element: rootEl,
      extensions: [
        StarterKit, TableKit
      ],
      content: html,
      onTransaction: () => {
        // 强制重新渲染,以确保 `editor.isActive` 正常工作
        editor = editor;
      },
      onUpdate: ({ editor }) => {
        html = lastHTML = editor.getHTML();
      },
      editable: true
    });
    lastHTML = html;
  }

  export function forceReload() {
    if (editor) {
      editor.destroy();
    }
    createEditor();
  }

  // 当 html 是由外部修改而不是编辑器本身修改时
  $: html != lastHTML && forceReload();

  onDestroy(() => {
    if (editor) {
      editor.destroy();
    }
  });
</script>

<div bind:this={rootEl} class="html-editor"></div>

添加控制面板

问题:添加按钮时报错

我第一次添加控制按钮(例如加粗、斜体)时遇到了如下错误:

The editor view is not available. Cannot access view['hasFocus']. The editor may not be mounted yet.

	in <unknown>
	in Tiptap.svelte
	in +page.svelte
	in +layout.svelte
	in root.svelte

后来我发现问题出在这行代码:

disabled={!editor.can().chain().focus().toggleBold().run()}

这是因为我们在编辑器尚未初始化完成之前调用了 editor.can() 方法。
解决方法是:

  • 要么删除 disabled 属性

  • 要么在调用之前增加编辑器就绪的判断。


示例:自定义控制面板

.html-editor 之前添加以下代码:

{#if editor}
  <div class="control-group">
    <!-- 文本格式控制 -->
    <div class="button-group">
      <button
        type="button"
        on:click={() => editor.chain().focus().toggleBold().run()}
        class={editor.isActive("bold") ? "is-active" : ""}
      >
        Bold
      </button>
      ...
      <!-- 其余按钮代码保持不变 -->
    </div>

    <div class="separator"></div>

    <!-- 表格控制 -->
    <div class="button-group">
      <button type="button" on:click={() => editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()}>
        Insert table
      </button>
      ...
    </div>
  </div>
{/if}

自定义编辑器内容样式

Tiptap 不会提供任何内置样式,你需要自己定义。
可以使用 class 或 CSS 选择器来自定义编辑器内容。

示例 CSS
.html-editor {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0.5rem;
}
.html-editor :global(.ProseMirror) {
  height: 100%;
  flex: 1;
}
.html-editor :global(.ProseMirror:focus-visible) {
  outline: none;
}

.control-group {
  position: sticky;
  top: 0;
  z-index: 10;
  background: #f5f5f5;
  border-bottom: 1px solid #ddd;
  padding: 0.5rem;
  margin-bottom: 0;
  flex-shrink: 0;
}

.button-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-bottom: 0.25rem;
}

.button-group button {
  padding: 0.25rem 0.5rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.2;
  color: #333;
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 0.25rem;
  cursor: pointer;
  transition: background-color 0.15s ease;
  min-width: auto;
  white-space: nowrap;
}

.button-group button:hover {
  background-color: #f0f0f0;
}

.button-group button:focus {
  outline: 2px solid #007acc;
  outline-offset: 1px;
}

.button-group button.is-active {
  background-color: #333;
  color: white;
  border-color: #333;
}

.button-group button.is-active:hover {
  background-color: #555;
  border-color: #555;
}

.separator {
  width: 100%;
  height: 0.25rem;
}

添加额外扩展

默认情况下,Tiptap 文本编辑器不包含 链接、表格、图片 等功能。
要添加这些功能,你需要安装相应的扩展。


结论

Tiptap 一旦运行起来,就是一个非常优秀的编辑器,但在与 Svelte 集成时,需要一些额外的处理,以及对 Tiptap 状态管理和命令机制的理解。

本教程演示了:

  • 如何在 Svelte 中初始化 Tiptap

  • 如何在编辑器与外部传递数据

  • 如何构建功能性控制面板

  • 如何自定义编辑器和控制按钮样式

希望这些内容能弥补官方文档的不足,节省你自己摸索的时间。