npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tiptap-editor-vue3

v2.0.8

Published

A vue3 high quality rich text editor

Readme

tiptap-editor-vue3

简体中文 | English

基于 Tiptap 3 + Vue 3 + TypeScript + Vite 开发的高质量富文本编辑器组件库。

  • 开箱即用:工具栏、气泡菜单、表格右键菜单、目录侧栏、图片浮动选中等全部内置
  • 内置中英文国际化(基于 vue-i18n)
  • 支持浅色 / 深色 / 跟随系统三种主题,CSS 变量可自由定制
  • 完整 TypeScript 类型,支持按需导入与 Hooks 二次组装
  • 在线演示:https://zekofront.github.io/tiptap-editor-vue3/

功能清单

| 分类 | 已实现功能 | | ---- | ---------- | | 文字标记 | 加粗、斜体、下划线、删除线、高亮、字体颜色、背景颜色、上标、下标、行内代码 | | 块级节点 | 标题(H1–H6)、段落、无序列表、有序列表、任务列表(可勾选)、引用块、分隔线、代码块(lowlight 语法高亮)、图片、表格、超链接、Emoji: 触发) | | 布局与行为 | 文本对齐(左 / 中 / 右 / 两端)、行间距、拖拽手柄(拖动块)、RTL 阿拉伯文方向 | | 编辑体验 | 顶部工具栏、选区气泡菜单、图片气泡菜单、表格右键菜单、目录侧栏(文档导航)、占位符、字符数限制、撤销 / 重做、清空文档、导入文档(TXT、DOC、DOCX)、导出 DOCX | | 组件级能力 | 只读视图(TiptapEditorView)、useEditor / useEditorEvents Hooks 自行组装、浅色 / 深色 / 系统主题、zh-CN / en-US 国际化、可自定义气泡菜单按钮、CSS 变量主题定制 |


安装

npm install tiptap-editor-vue3
# 或
pnpm add tiptap-editor-vue3
# 或
yarn add tiptap-editor-vue3

项目需要 Vue 3 环境(仅 Vue 为外部依赖,其它依赖如 Tiptap、naive-ui、vue-i18n 等已打入产物)。


快速开始

方式一:全局安装(推荐)

// main.ts
import { createApp } from "vue";
import TiptapEditorVue3 from "tiptap-editor-vue3";
import "tiptap-editor-vue3/dist/css/style.css";
import App from "./App.vue";

const app = createApp(App);
app.use(TiptapEditorVue3);
app.mount("#app");
<!-- App.vue -->
<template>
    <TiptapEditorVue3
        v-model="content"
        :is-editable="true"
        :character-count="20000"
        locale="zh-CN"
        theme="system"
        placeholder="请输入内容..."
        @ready="onReady"
        @update="onUpdate"
    />
</template>

<script setup lang="ts">
import { ref } from "vue";
import type { Editor, EditorUpdatePayload } from "tiptap-editor-vue3";

const content = ref("<p>初始内容</p>");

const onReady = (editor: Editor) => {
    console.log("editor ready", editor);
};

const onUpdate = ({ html, json }: EditorUpdatePayload) => {
    console.log(html, json);
};
</script>

使用 v-model 双向绑定内容。传入 HTML 字符串(默认)或 Tiptap JSON 对象;通过 outputFormat prop 切换 update:modelValue 发送的格式。

方式二:按需导入

<template>
    <TiptapEditorVue3 v-model:is-editable="editable" @update="onUpdate" />
</template>

<script setup lang="ts">
import { ref } from "vue";
import { TiptapEditorVue3, type EditorUpdatePayload } from "tiptap-editor-vue3";
import "tiptap-editor-vue3/dist/css/style.css";

const editable = ref(true);
const onUpdate = (p: EditorUpdatePayload) => console.log(p.html);
</script>

只读视图

<template>
    <TiptapEditorView :editor="editor" />
</template>

<script setup lang="ts">
import { TiptapEditorView, useEditor } from "tiptap-editor-vue3";
import StarterKit from "@tiptap/starter-kit";

const { editor } = useEditor({
    extensions: () => [StarterKit],
    content: () => "<p>Hello</p>",
    editable: () => false
});
</script>

Props(参数列表)

| 参数 | 类型 | 默认值 | 说明 | | ---- | ---- | ------ | ---- | | modelValue (v-model) | string \| JSONContent \| null | "" | 编辑器内容;支持 HTML 字符串或 Tiptap JSON | | outputFormat | 'html' \| 'json' | 'html' | update:modelValue 发送的格式 | | defaultConfig | Record<string, any> \| null | null | 透传给底层 new Editor(...) 的额外配置 | | extensions | AnyExtension[] | [] | 自定义扩展数组;不传则使用内置完整扩展集 | | isEditable | boolean | true | 是否可编辑 | | isShowToolbar | boolean | true | 是否显示顶部工具栏 | | isEnabledContent | boolean | true | 渲染内容区(关闭时仅工具栏) | | characterCount | number \| string | 10000 | 字符数限制;超过禁止输入 | | locale | 'zh-CN' \| 'en-US' | 'zh-CN' | 界面语言(工具栏提示、表格菜单、占位符等) | | theme | 'system' \| 'light' \| 'dark' | 'system' | 主题色彩;system 跟随操作系统 prefers-color-scheme | | placeholder | string | i18n 默认值 | 空文档占位符;不传时使用当前语言默认值 | | customDubbleMenus | IDubbleMenu[] | [] | 自定义气泡菜单按钮列表 | | editorWrapperClass | string \| any[] \| Record<string, any> | undefined | 最外层容器自定义类 | | editorToolkitClass | string \| any[] \| Record<string, any> | undefined | 工具栏自定义类 | | editorContentClass | string | 'custom-tiptap-content' | 内容区 ProseMirror 容器类 | | editorContentsNavClass | string \| any[] \| Record<string, any> | undefined | 目录侧栏自定义类 |

IDubbleMenu 等类型从包内导出:import type { IDubbleMenu, EditorProps } from "tiptap-editor-vue3"


Events(事件列表)

| 事件 | 负载(payload) | 说明 | | ---- | --------------- | ---- | | update:modelValue | string \| JSONContent | 内容变化(v-model 同步);默认 HTML 字符串,outputFormat="json" 时为 JSON | | ready | editor: Editor | 编辑器实例创建完成,等价于 Tiptap create | | update | { editor, html, json } | 内容变化;html / json 已自动获取,无需再调用 editor.getHTML() | | selection-update | { editor } | 光标 / 选区变化 | | transaction | { editor, transaction } | 任意 ProseMirror 事务 | | focus | { editor, event: FocusEvent } | 编辑器获得焦点 | | blur | { editor, event: FocusEvent } | 编辑器失去焦点 | | before-create | { editor } | 实例创建前 | | content-error | { editor, error } | 解析初始内容失败 | | destroy | — | 实例销毁 |

update 事件负载类型:

import type { EditorUpdatePayload } from "tiptap-editor-vue3";

interface EditorUpdatePayload {
    editor: Editor;
    html: string;
    json: JSONContent;
}

导入文档(TXT 和 Word)

编辑器支持通过工具栏中的导入按钮导入 TXT 和 Word(DOC、DOCX)文档的内容。点击导入按钮后,会出现一个下拉菜单,可以选择导入 TXT 文件或 Word 文件。选择文件后,内容会被解析并插入到编辑器中。成功和错误信息会使用本地化的消息进行提示。

图片上传

图片上传统一通过 defaultConfig.uploadImage 配置,规则非常简单:

  • imageLink / customUpload 是函数 → 走用户自定义逻辑,弹窗把 URL / 文件交给你,由你自己 editor.commands.setImage(...) 插入;
  • 否则 → 走组件内部默认实现(链接 Tab 直接 setImage({ src })、上传 Tab 转 base64 插入)。

accept / maxSize / maxCount 始终生效,组件先做校验再交给用户回调;用户没选图片就点"立即上传"时会弹本地化提示。

自定义图片上传,严格遵循下面配置规则,否则不生效

<template>
    <TiptapEditorVue3
        v-model="content"
        :default-config="defaultConfig"
        @ready="onReady"
    />
</template>

<script setup lang="ts">
import { shallowRef } from "vue";
import type { Editor, Tev3DefaultConfig } from "tiptap-editor-vue3";

const editors = shallowRef<Editor | null>(null);

const onReady = (editor: Editor) => {
    editors.value = editor;
};

const defaultConfig: Tev3DefaultConfig = {
    uploadImage: {
        accept: "image/png,image/jpeg,image/webp",
        maxSize: 5 * 1024 * 1024, // 单张最大 5MB
        maxCount: 9,
        imageLink: (link: string) => {
            console.log(link, editors.value, "imageLink");
            editors.value?.commands.setImage({ src: link });
        },
        customUpload: async (files) => {
            console.log(files, editors.value, "customUpload");
            for (let i = 0; i < files.length; i++) {
                if (files[i]) setImageOne(files[i] as File);
            }
        }
    }
};

const setImageOne = (file: File) => {
    const reader = new FileReader();
    reader.onload = event => {
        const base64 = event.target?.result as string;
        editors.value?.commands.setImage({ src: base64 });
    };
    reader.readAsDataURL(file);
};
</script>

| 字段 | 类型 | 默认值 | 说明 | | ---- | ---- | ------ | ---- | | accept | string | image/png,image/jpeg,image/gif,image/webp,image/svg+xml | HTML accept 语法,过滤选择器并校验类型 | | maxSize | number | 10 * 1024 * 1024 | 单张图片大小上限(字节),超过弹错误提示并跳过 | | maxCount | number | Infinity | 单次最多选择图片数量 | | imageLink | (url: string) => void | — | "图片链接" 选项卡确认时的回调。是函数 → 走用户自定义;否则走默认 setImage({ src }) | | customUpload | (files: File[] \| FileList) => void \| Promise<void> | — | "上传图片" 选项卡确认时的回调。是函数 → 走用户自定义;否则文件转 base64 直接插入 |

导出类型:Tev3DefaultConfigTev3UploadImageConfig


主题(明暗模式)

通过 theme prop 控制:

<TiptapEditorVue3 theme="system" />
<TiptapEditorVue3 theme="light" />
<TiptapEditorVue3 theme="dark" />
  • system:自动跟随系统 prefers-color-scheme,系统切换会实时响应;
  • light / dark:固定使用对应主题。

底层通过 data-tev3-theme="light|dark" 数据属性切换,所有颜色基于 CSS 变量。如需自定义品牌色,覆盖以下常用变量即可:

.vue3-tiptap-editor[data-tev3-theme="light"] {
    --theme-color: #2080f0;
    --tev3-toolbar-bg: #fafbfc;
}

.vue3-tiptap-editor[data-tev3-theme="dark"] {
    --theme-color: #36ad6a;
    --tev3-editor-bg: #15161a;
}

JS 端可使用 useTheme() 自行实现切换:

import { ref } from "vue";
import { useTheme, type Tev3Theme } from "tiptap-editor-vue3";

const theme = ref<Tev3Theme>("system");
const { resolvedTheme } = useTheme(() => theme.value); // -> 'light' | 'dark'

国际化

内置 zh-CN / en-US,通过 locale prop 切换;任意时刻也可调用 setLocale() 全局切换。

import { setLocale, t } from "tiptap-editor-vue3";

setLocale("en-US");
console.log(t("toolbar.bold")); // "Bold"
<TiptapEditorVue3 locale="en-US" />

切换 locale 时工具栏、表格菜单、目录、占位符会自动刷新。

如需自定义文案,可在自己的项目中调用 tev3I18n.global.mergeLocaleMessage('zh-CN', { ... }) 覆盖。


进阶:使用 Hooks 自行组装

若内置工具栏不满足需求,可直接复用 useEditor / useEditorEvents 自己组合 UI:

<template>
    <div>
        <button @click="editor?.chain().focus().toggleBold().run()">Bold</button>
        <TiptapEditorView :editor="editor" />
    </div>
</template>

<script setup lang="ts">
import {
    TiptapEditorView,
    useEditor,
    useEditorEvents,
    type EditorUpdatePayload
} from "tiptap-editor-vue3";
import StarterKit from "@tiptap/starter-kit";

const emit = defineEmits<{ (e: "update", p: EditorUpdatePayload): void }>();

const { editor, editable, toggleEditable } = useEditor({
    extensions: () => [StarterKit],
    content: () => "<p>Hello</p>",
    editable: () => true
});

useEditorEvents(editor, { emit });
</script>

useEditor 返回:

| 字段 | 类型 | 说明 | | ---- | ---- | ---- | | editor | ShallowRef<Editor \| null> | 实例(onMounted 后就绪) | | editable | Ref<boolean> | 可编辑状态 | | rtl | Ref<boolean> | RTL 排版 | | toggleEditable | () => void | 切换只读 | | toggleRtl | () => void | 切换 RTL | | create / destroy | () => void | 手动控制生命周期 |


包导出

// 组件
export { TiptapEditorVue3, TiptapEditorView };

// Composables
export { useEditor, useEditorEvents, useTev3I18n };

// Props / 类型
export { editorProps };
export type {
    EditorProps,
    UseEditorOptions,
    UseEditorReturn,
    EditorUpdatePayload,
    EditorEventName,
    Tev3Locale
};

// 国际化
export { tev3I18n, t, setLocale, getLocale, installTev3I18n, SUPPORTED_LOCALES, zhCN, enUS };

// 主题
export { SUPPORTED_THEMES, DEFAULT_THEME, resolveTheme, useTheme };
export type { Tev3Theme, Tev3ResolvedTheme };

// Tiptap 常用 API(避免重复装包)
export { Editor, EditorContent, NodeViewWrapper, mergeAttributes };
export type { TiptapEditor, JSONContent, Content, AnyExtension };

开发命令

npm run dev           # 启动本地 playground
npm run build         # 构建组件库(dist/)
npm run build:demo    # 构建 playground 演示站(dist-demo/)
npm run preview:demo  # 本地预览演示站
npm run fmt           # 格式化(oxfmt)
npm run lint          # 代码检查(oxlint)

在线演示(GitHub Pages)

仓库已配置 .github/workflows/deploy-pages.yml,推送 main 分支后会自动构建并发布 dist-demo

GitHub 仓库需在 Settings → Pages → Source 选择 GitHub Actions,演示地址:

https://zekofront.github.io/tiptap-editor-vue3/


参考链接


License

MIT