{"id":16329812,"url":"https://github.com/yikoyu/vuetify-pro-tiptap","last_synced_at":"2026-04-01T20:44:59.646Z","repository":{"id":36984035,"uuid":"501596185","full_name":"yikoyu/vuetify-pro-tiptap","owner":"yikoyu","description":"A Rich Text Editor (WYSIWYG) for Vue3 with tiptap \u0026 Vuetify.","archived":false,"fork":false,"pushed_at":"2026-01-01T07:08:47.000Z","size":2662,"stargazers_count":261,"open_issues_count":8,"forks_count":48,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-01-05T11:09:33.665Z","etag":null,"topics":["editor","html-editor","markdown","material-design","prosemirror","rich-text","rich-text-editor","tiptap","tiptap-editor","typescript","vue","vuetify","vuetify-editor","wysiwyg","wysiwyg-editor"],"latest_commit_sha":null,"homepage":"https://yikoyu.github.io/vuetify-pro-tiptap/master/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yikoyu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-06-09T09:54:51.000Z","updated_at":"2026-01-04T14:18:12.000Z","dependencies_parsed_at":"2023-10-29T20:36:25.306Z","dependency_job_id":"c10495c7-ff8d-4aa5-b2f1-44ca43674fb0","html_url":"https://github.com/yikoyu/vuetify-pro-tiptap","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/yikoyu/vuetify-pro-tiptap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yikoyu%2Fvuetify-pro-tiptap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yikoyu%2Fvuetify-pro-tiptap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yikoyu%2Fvuetify-pro-tiptap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yikoyu%2Fvuetify-pro-tiptap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yikoyu","download_url":"https://codeload.github.com/yikoyu/vuetify-pro-tiptap/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yikoyu%2Fvuetify-pro-tiptap/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["editor","html-editor","markdown","material-design","prosemirror","rich-text","rich-text-editor","tiptap","tiptap-editor","typescript","vue","vuetify","vuetify-editor","wysiwyg","wysiwyg-editor"],"created_at":"2024-10-10T23:17:18.049Z","updated_at":"2026-04-01T20:44:59.636Z","avatar_url":"https://github.com/yikoyu.png","language":"TypeScript","funding_links":[],"categories":["Vue.js","TypeScript"],"sub_categories":[],"readme":"# Vuetify Pro Tiptap\n\nA WYSIWYG rich-text editor using [tiptap](https://github.com/scrumpy/tiptap) and [vuetify](https://github.com/vuetifyjs/vuetify) for Vue.js\n\n[![download](https://img.shields.io/npm/dm/vuetify-pro-tiptap.svg)](https://npmcharts.com/compare/vuetify-pro-tiptap?minimal=true)\n[![version](https://img.shields.io/npm/v/vuetify-pro-tiptap.svg)](https://www.npmjs.org/package/vuetify-pro-tiptap)\n[![gzip](https://img.badgesize.io/https://unpkg.com/vuetify-pro-tiptap/lib/vuetify-pro-tiptap.js?compression=gzip\u0026label=gzip)](https://img.badgesize.io/https://unpkg.com/vuetify-pro-tiptap/lib/vuetify-pro-tiptap.js?compression=gzip\u0026label=gzip)\n[![Test](https://github.com/yikoyu/vuetify-pro-tiptap/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/yikoyu/vuetify-pro-tiptap/actions/workflows/test.yml)\n![LICENSE](https://img.shields.io/badge/License-MIT-yellow.svg)\n[![semantic-release: vue](https://img.shields.io/badge/semantic--release-vue-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)\n[![pkg.pr.new](https://pkg.pr.new/badge/yikoyu/vuetify-pro-tiptap)](https://pkg.pr.new/~/yikoyu/vuetify-pro-tiptap)\n\nEnglish | [中文](./README.zh-CN.md)\n\n## Demo\n👉[https://yikoyu.github.io/vuetify-pro-tiptap/master/](https://yikoyu.github.io/vuetify-pro-tiptap/master/)\n\n👉[https://yikoyu.github.io/vuetify-pro-tiptap/develop/](https://yikoyu.github.io/vuetify-pro-tiptap/develop/)\n\n👾[Stackblitz](https://stackblitz.com/~/github.com/yikoyu/vuetify-pro-tiptap-demo/tree/main)\n\n👾[Nuxt3 Stackblitz](https://stackblitz.com/~/github.com/yikoyu/vuetify-pro-tiptap-demo/tree/nuxt)\n\n## Features\n- Use [vuetify](https://github.com/vuetifyjs/vuetify) components\n- Many out of box [extension](#extensions) (welcome to submit an issue for feature request)\n- Markdown support\n- TypeScript support\n- I18n support(`en`, `zhHans`, `nl`, `tr`, `de`, `fr`, `it`, `pt`, `hu`)\n- Vuetify 3.x and Vue 3.x support\n\n## Vuetify 2.x\nFor Vuetify 2.x please use the latest version of [vuetify-pro-tiptap@1.X.X](https://github.com/yikoyu/vuetify-pro-tiptap/tree/1.x)\n\n## Installation\n### NPM\n```shell\npnpm add vuetify-pro-tiptap\n# or\nyarn add vuetify-pro-tiptap\n# or\nnpm i vuetify-pro-tiptap -S\n```\n\n### Install plugin\n\n\u003cdetails\u003e\n\u003csummary\u003etiptap.ts\u003c/summary\u003e\n\n```TypeScript\nimport { markRaw } from 'vue'\nimport { VuetifyTiptap, VuetifyViewer, createVuetifyProTipTap } from 'vuetify-pro-tiptap'\nimport { BaseKit, Bold, Italic, Underline, Strike, Color, Highlight, Heading, TextAlign, FontFamily, FontSize, SubAndSuperScript, BulletList, OrderedList, TaskList, Indent, Link, Image, Video, Table, Blockquote, HorizontalRule, Code, CodeBlock, Clear, Fullscreen, History } from 'vuetify-pro-tiptap'\nimport 'vuetify-pro-tiptap/style.css'\nimport SelectImage from './components/SelectImage.vue'\n\nexport const vuetifyProTipTap = createVuetifyProTipTap({\n  lang: 'zhHans',\n  fallbackLang: 'en',\n  components: {\n    VuetifyTiptap,\n    VuetifyViewer\n  },\n  extensions: [\n    BaseKit.configure({\n      placeholder: {\n        placeholder: 'Enter some text...'\n      }\n    }),\n    Bold,\n    Italic,\n    Underline,\n    Strike,\n    Code.configure({ divider: true }),\n    Heading,\n    TextAlign,\n    FontFamily,\n    FontSize,\n    Color,\n    Highlight.configure({ divider: true }),\n    SubAndSuperScript.configure({ divider: true }),\n    Clear.configure({ divider: true }),\n    BulletList,\n    OrderedList,\n    TaskList,\n    Indent.configure({ divider: true }),\n    Link,\n    Image.configure({\n      imageTabs: [{ name: 'SELECT', component: markRaw(SelectImage) }],\n      // hiddenTabs: ['upload'],\n      upload(file: File) {\n        const url = URL.createObjectURL(file)\n        console.log('mock upload api :\u003e\u003e ', url)\n        return Promise.resolve(url)\n      }\n    }),\n    Video,\n    Table.configure({ divider: true }),\n    Blockquote,\n    HorizontalRule,\n    CodeBlock.configure({ divider: true }),\n    History.configure({ divider: true }),\n    Fullscreen\n  ]\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003emain.ts\u003c/summary\u003e\n\n```typescript\nimport { createApp } from 'vue'\nimport { createVuetify } from 'vuetify'\nimport App from './App.vue'\nimport { vuetifyProTipTap } from './tiptap'\n\nimport 'vuetify/styles'\n\nconst vuetify = createVuetify()\n\nconst app = createApp(App)\napp.use(vuetify)\napp.use(vuetifyProTipTap)\n\n// fix warning injected property \"decorationClasses\" is a ref and will be auto-unwrapped\n// https://github.com/ueberdosis/tiptap/issues/1719\napp.config.unwrapInjectedRef = true\n\napp.mount('#app')\n```\n\n\u003c/details\u003e\n\n## Global Settings\n```TypeScript\nimport { markRaw } from 'vue'\nimport { VuetifyTiptap, VuetifyViewer, createVuetifyProTipTap, defaultBubbleList } from 'vuetify-pro-tiptap'\nimport { BaseKit, Image, Fullscreen } from 'vuetify-pro-tiptap'\nimport 'vuetify-pro-tiptap/style.css'\nimport SelectImage from './components/SelectImage.vue'\n\nexport const vuetifyProTipTap = createVuetifyProTipTap({\n  // Set default lang\n  lang: 'zhHans',\n  // Set default fallback lang\n  fallbackLang: 'en',\n  // Set markdown theme\n  markdownTheme: 'github',\n  // Global registration app.component\n  components: {\n    VuetifyTiptap,\n    VuetifyViewer\n  },\n  // Global registration extensions\n  extensions: [\n    BaseKit.configure({\n      placeholder: {\n        placeholder: 'Enter some text...'\n      },\n      bubble: {\n        // default config\n        list: {\n          image: [ 'float-left', 'float-none', 'float-right', 'divider', 'size-small', 'size-medium', 'size-large', 'divider', 'textAlign', 'divider', 'image', 'image-aspect-ratio', 'remove'],\n          text: ['bold', 'italic', 'underline', 'strike', 'divider', 'color', 'highlight', 'textAlign', 'divider', 'link'],\n          video: ['video', 'remove']\n        },\n        defaultBubbleList: editor =\u003e {\n          // You can customize the bubble menu here\n          return defaultBubbleList(editor) // default customize bubble list\n        }\n      }\n    }),\n    Image.configure({\n      // Generate a VDivider after the button\n      divider: true,\n      // Custom image tabs\n      imageTabs: [{ name: 'SELECT', component: markRaw(SelectImage) }],\n      // hidden default tab\n      hiddenTabs: ['upload'],\n      // custom upload function\n      upload(file) {\n        const url = URL.createObjectURL(file)\n        console.log('mock upload api :\u003e\u003e ', url)\n        return Promise.resolve(url)\n      }\n    }),\n    Fullscreen.configure({\n      // Generate a VSpacer after the button\n      spacer: true\n    })\n  ]\n})\n```\n\n## \u003cspan id=\"extensions\"\u003eExtensions\u003c/span\u003e\n\nYou can use the necessary extensions. The corresponding command-buttons will be added by declaring the order of the extension.\n\nAll available extensions：\n\n- [`BaseKit`](./src/extensions/base-kit.ts)\n- [`Bold`](./src/extensions/bold.ts)\n- [`Italic`](./src/extensions/italic.ts)\n- [`Underline`](./src/extensions/underline.ts)\n- [`Strike`](./src/extensions/strike.ts)\n- [`Color`](./src/extensions/color.ts)\n- [`Highlight`](./src/extensions/highlight.ts)\n- [`Heading`](./src/extensions/heading.ts)\n- [`TextAlign`](./src/extensions/text-align.ts)\n- [`FontFamily`](./src/extensions/font-family.ts)\n- [`FontSize`](./src/extensions/font-size.ts)\n- [`SubAndSuperScript`](./src/extensions/subscript.ts)\n- [`BulletList`](./src/extensions/bullet-list.ts)\n- [`OrderedList`](./src/extensions/ordered-list.ts)\n- [`TaskList`](./src/extensions/task-list.ts)\n- [`Indent`](./src/extensions/indent.ts)\n- [`Link`](./src/extensions/link.ts)\n- [`MarkdownTheme`](./src/extensions/markdown-theme.ts)\n- [`Image`](./src/extensions/image.ts)\n- [`Video`](./src/extensions/video.ts)\n- [`Table`](./src/extensions/table.ts)\n- [`Blockquote`](./src/extensions/blockquote.ts)\n- [`HorizontalRule`](./src/extensions/horizontal-rule.ts)\n- [`Code`](./src/extensions/code.ts)\n- [`CodeBlock`](./src/extensions/code-block.ts)\n- [`Clear`](./src/extensions/clear.ts)\n- [`Fullscreen`](./src/extensions/fullscreen.ts)\n- [`History`](./src/extensions/history.ts)\n\n## Custom theme\n\nCreate github.scss\n\n```scss\n$value: 'github';\n\n.vuetify-pro-tiptap-editor__content.markdown-theme-#{$value} {\n  \u0026.__dark {\n    // your dark mode custom styles\n  }\n\n  .tiptap.ProseMirror,\n  .content {\n    // your custom styles\n  }\n}\n```\n\nImport github.scss in ts\n\n```typescript\n// import 'vuetify-pro-tiptap/style.css' // import all(editor and markdown) styles\nimport 'vuetify-pro-tiptap/styles/editor.css' // only use editor style, not using markdown style\nimport './styles/markdown/github.scss'\n```\n\nIn the component using\n\n```vue\n\u003ctemplate\u003e\n  \u003cVuetifyTiptap v-model=\"content\" markdown-theme=\"github\" /\u003e\n  \u003cVuetifyViewer :value=\"content\" markdown-theme=\"github\" /\u003e\n\u003c/template\u003e\n```\n\n## Custom extensions\n\n\u003cdetails\u003e\n\u003csummary\u003ePreviewActionButton.vue\u003c/summary\u003e\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport type { Editor } from '@tiptap/vue-3'\nimport { mdiClose, mdiFileCodeOutline } from '@mdi/js'\nimport { ref } from 'vue'\nimport { ActionButton } from 'vuetify-pro-tiptap'\n\nconst props = withDefaults(defineProps\u003c{\n  editor: Editor\n  tooltip?: string\n  disabled?: boolean\n}\u003e(), {\n  tooltip: undefined,\n  disabled: false\n})\n\nconst dialog = ref(false)\nconst maxWidth = ref\u003cnumber\u003e(900)\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cActionButton :editor=\"editor\" tooltip=\"全屏\" :disabled=\"disabled\"\u003e\n    \u003cVIcon\u003e{{ `svg:${mdiFileCodeOutline}` }}\u003c/VIcon\u003e\n    \u003cVDialog v-model=\"dialog\" fullscreen hide-overlay activator=\"parent\"\u003e\n      \u003cVCard\u003e\n        \u003cVToolbar dark color=\"primary\"\u003e\n          \u003cVBtn icon dark @click=\"dialog = false\"\u003e\n            \u003cVIcon\u003e{{ `svg:${mdiClose}` }}\u003c/VIcon\u003e\n          \u003c/VBtn\u003e\n        \u003c/VToolbar\u003e\n\n        \u003cVContainer\u003e\n          \u003cVSheet class=\"mx-auto\" :max-width=\"maxWidth\"\u003e\n            \u003cVuetifyViewer :value=\"editor.getHTML()\" /\u003e\n          \u003c/VSheet\u003e\n        \u003c/VContainer\u003e\n      \u003c/VCard\u003e\n    \u003c/VDialog\u003e\n  \u003c/ActionButton\u003e\n\u003c/template\u003e\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003epreview.ts\u003c/summary\u003e\n\n```typescript\nimport type { GeneralOptions } from 'vuetify-pro-tiptap'\nimport { Extension } from '@tiptap/core'\n\nimport PreviewActionButton from '../components/PreviewActionButton.vue'\n\nexport type PreviewOptions = GeneralOptions\u003cPreviewOptions\u003e\n\nexport default Extension.create\u003cPreviewOptions\u003e({\n  name: 'preview',\n  addOptions() {\n    return {\n      divider: false,\n      spacer: false,\n      button: ({ editor }) =\u003e ({\n        component: PreviewActionButton,\n        componentProps: {\n          editor,\n        },\n      }),\n    }\n  },\n})\n```\n\n\u003c/details\u003e\n\n## I18n\n\n### Setting language\nYou can declare when you install the plugin.\n```TypeScript\nimport { createVuetifyProTipTap } from 'vuetify-pro-tiptap'\n\nconst VuetifyProTipTap = createVuetifyProTipTap({\n  lang: 'zhHans',\n  fallbackLang: 'en'\n})\n```\nOr use `setLang` dynamic change\n```TypeScript\nimport { locale } from 'vuetify-pro-tiptap'\n\nlocale.setLang('en')\n```\nAvailable languages:\n- en (default)\n- zhHans\n- nl\n- tr\n- de\n- fr\n- it\n- pt\n- hu\n\n### Use unavailable language\nLoading unavailable language, use `setMessage` for Settings\n```TypeScript\nimport { locale } from 'vuetify-pro-tiptap'\n\nlocale.setMessage('zhHant', {\n  // i18n text\n})\nlocale.setLang('zhHant')\n```\n\n## Usage\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { ref } from 'vue'\nimport { BaseKit, Bold, Color, Fullscreen, Heading, Highlight, History, Image, Italic, Link, Strike, Table, Underline, Video, VuetifyTiptap, VuetifyViewer } from 'vuetify-pro-tiptap'\nimport 'vuetify-pro-tiptap/style.css'\n\nconst extensions = [\n  BaseKit.configure({\n    placeholder: {\n      placeholder: 'Enter some text...'\n    }\n  }),\n  Bold,\n  Italic,\n  Underline,\n  Strike,\n  Color,\n  Highlight,\n  Heading,\n  Link,\n  Image,\n  Video,\n  Table,\n  Fullscreen,\n  History\n]\n\nconst content = ref('')\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cVApp id=\"app\"\u003e\n    \u003cVContainer\u003e\n      \u003cVuetifyTiptap v-model=\"content\" label=\"Title\" rounded :min-height=\"200\" :max-height=\"465\" :max-width=\"900\" :extensions=\"extensions\" /\u003e\n      \u003cVuetifyViewer :value=\"content\" /\u003e\n    \u003c/VContainer\u003e\n  \u003c/VApp\u003e\n\u003c/template\u003e\n```\n\n## Props\n\n### VuetifyTiptap\n\n#### Props\n| Name | Type | Default | Description |\n| ---- | ---- | ---- | ---- |\n| modelValue | string \\| JSONContent | '' | The input’s value |\n| markdownTheme | string \\| false | 'default' | Markdown theme |\n| output | 'html' \\| 'json' \\| 'text' | 'html' | Output format |\n| dark | boolean | false | Applies the dark theme variant to the component. |\n| dense | boolean | false | Reduces the input height |\n| outlined | boolean | true | Applies the outlined style to the input |\n| flat | boolean | true | Removes the card’s elevation |\n| disabled | boolean | false | Disable the input |\n| label | string | undefined | Sets input label |\n| hideToolbar | boolean | false | Hidden the toolbar |\n| disableToolbar | boolean | false | Disable the toolbar |\n| hideBubble | boolean | false | Hidden the bubble menu |\n| removeDefaultWrapper | boolean | false | Default wrapper when the delete editor is empty |\n| maxWidth | string \\| number | undefined | Sets the maximum width for the component. |\n| minHeight | string \\| number | undefined | Sets the minimum height for the component. |\n| maxHeight | string \\| number | undefined | Sets the maximum height for the component. |\n| extensions | AnyExtension[] | [] | Tiptap the extensions |\n| editorClass | string \\| string[] \\| Record\\\u003cstring, any\u003e | undefined | Editor class |\n\n#### Slots\n| Name | Description |\n| ---- | ---- |\n| editor | Slot to customize editor |\n| bottom | Slot to customize editor bottom |\n\n#### Event\n| Name | Type | Description |\n| ---- | ---- | ---- |\n| update:modelValue | string \\| JSONContent | Emitted when editor onUpdate |\n| update:markdownTheme | string | Emitted when change theme |\n| change | { editor: Editor, output: string \\| JSONContent } | Emitted when editor onUpdate |\n| enter | | Keyboard enter return |\n\n### VuetifyViewer\n\n#### Props\n| Name | Type | Default | Description |\n| ---- | ---- | ---- | ---- |\n| value | string \\| JSONContent | '' | The preview’s value |\n| dark | boolean | false | Applies the dark theme variant to the component. |\n| dense | boolean | false | Reduces the input height |\n| markdownTheme | string \\| false | 'default' | Markdown theme |\n| xss | boolean | true | Enable xss filter |\n| xssOptions | xss.IWhiteList | Default rule | Xss filter rule config |\n| maxWidth | string \\| number | undefined | Sets the maximum width for the component. |\n| extensions | AnyExtension[] | [] | Tiptap the extensions |\n\n#### Slots\n| Name | Description |\n| ---- | ---- |\n| before | Add content at the before |\n| after | Add content at the after |\n\n## 🏗 Contributing\n\n1. 🍴Fork it\n2. 🔀Create your branch: `git checkout -b your-branch`\n3. 🎨Make your changes\n4. 📝Commit your changes with [Semantic Commit Messages (recommended)](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716)\n5. 🚀Push to the branch: `git push origin your-branch`\n6. 🎉Submit a PR to `develop` branch\n\n## 📄 License\n\n[MIT](./LICENSE)\n\n## Thanks\n\n- [vuetify](https://github.com/vuetifyjs/vuetify)\n- [tiptap](https://github.com/scrumpy/tiptap)\n- [element-tiptap](https://github.com/Leecason/element-tiptap)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyikoyu%2Fvuetify-pro-tiptap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyikoyu%2Fvuetify-pro-tiptap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyikoyu%2Fvuetify-pro-tiptap/lists"}