diff --git a/docs/src/docPages/guide/getting-started/vue.md b/docs/src/docPages/guide/getting-started/vue.md
index 237b4f80..419dec7e 100644
--- a/docs/src/docPages/guide/getting-started/vue.md
+++ b/docs/src/docPages/guide/getting-started/vue.md
@@ -1,104 +1,393 @@
-# Vue.js
+# Vue.js 3
## toc
## Introduction
-The following guide describes how to integrate tiptap with your [Vue](https://vuejs.org/) CLI project.
+The `@tiptap/vue` package is not yet ported to Vue 3. Meanwhile, you can find compatible version contributed by [@samwillis](https://github.com/samwillis) and [@areknawo](https://github.com/areknawo) here.
-## Requirements
-* [Node](https://nodejs.org/en/download/) installed on your machine
-* [Vue CLI](https://cli.vuejs.org/) installed on your machine
-* Experience with [Vue](https://vuejs.org/v2/guide/#Getting-Started)
+## EditorContent.ts
+https://github.com/ueberdosis/tiptap-next/issues/85#issuecomment-774520164
-## 1. Create a project (optional)
-If you already have an existing Vue project, that’s fine too. Just skip this step and proceed with the next step.
+```ts
+import { defineComponent, h, ref, Teleport, onBeforeUpdate } from 'vue'
+import VueRenderer from '../VueRenderer'
-For the sake of this guide, let’s start with a fresh Vue project called `tiptap-example`. The Vue CLI sets up everything we need, just select the default Vue 2 template.
+function setupVueRenderers(){
+ const vueRenderers = ref(([] as VueRenderer[]))
+ const vueRendererEls = ref(new Map())
+ const addVueRenderer = (vueRenderer: VueRenderer) => {
+ vueRenderers.value.push(vueRenderer)
+ }
+ const deleteVueRenderer = (vueRenderer: VueRenderer) => {
+ const index = vueRenderers.value.indexOf(vueRenderer)
+ if (index > -1) {
+ vueRenderers.value.splice(index, 1)
+ }
+ }
+ onBeforeUpdate(() => {
+ vueRendererEls.value = new Map()
+ })
+ return {
+ vueRenderers,
+ vueRendererEls,
+ addVueRenderer,
+ deleteVueRenderer,
+ }
+}
-```bash
-# create a project
-vue create tiptap-example
+export default defineComponent({
+ name: 'EditorContent',
-# change directory
-cd tiptap-example
-```
-
-## 2. Install the dependencies
-Okay, enough of the boring boilerplate work. Let’s finally install tiptap! For the following example you’ll need `@tiptap/core` (the actual editor) and the `@tiptap/vue-starter-kit` which has everything to get started quickly, for example a few default extensions and a basic Vue component.
-
-```bash
-# install with npm
-npm install @tiptap/core @tiptap/vue-starter-kit
-
-# install with Yarn
-yarn add @tiptap/core @tiptap/vue-starter-kit
-```
-
-If you followed step 1 and 2, you can now start your project with `npm run dev` or `yarn dev`, and open [http://localhost:8080/](http://localhost:3000/) in your favorite browser. This might be different, if you’re working with an existing project.
-
-## 3. Create a new component
-To actually start using tiptap, you’ll need to add a new component to your app. Let’s call it `Tiptap` and put the following example code in `components/Tiptap.vue`.
-
-This is the fastest way to get tiptap up and running with Vue. It will give you a very basic version of tiptap, without any buttons. No worries, you will be able to add more functionality soon.
-
-```html
-
-
-
-
-
+})
```
-## 4. Add it to your app
-Now, let’s replace the content of `src/App.vue` with the following example code to use our new `Tiptap` component in our app.
+## VueRenderer.ts
-```html
-
-
-
-
-
+https://github.com/ueberdosis/tiptap-next/issues/85#issuecomment-774520164)
-
```
-You should now see tiptap in your browser. You’ve successfully set up tiptap! Time to give yourself a pat on the back. Let’s start to configure your editor in the next step.
+## BubbleMenu.ts
-## 5. Use v-model (optional)
-You’re probably used to bind your data with `v-model` in forms, that’s also possible with tiptap. Here is a working example component, that you can integrate in your project:
+https://github.com/ueberdosis/tiptap-next/issues/62#issuecomment-750914155
-
+```ts
+import { Editor } from '@tiptap/core'
+import { EditorState, Plugin, PluginKey } from 'prosemirror-state'
+import { EditorView } from 'prosemirror-view'
+
+interface BubbleMenuSettings {
+ bottom: number
+ isActive: boolean
+ left: number
+ top: number
+}
+interface BubbleMenuPluginOptions {
+ editor: Editor
+ element: HTMLElement
+ keepInBounds: boolean
+ onUpdate(menu: BubbleMenuSettings): void
+}
+type DOMRectSide = 'bottom' | 'left' | 'right' | 'top'
+
+function textRange(node: Node, from?: number, to?: number) {
+ const range = document.createRange()
+ range.setEnd(
+ node,
+ typeof to === 'number' ? to : (node.nodeValue || ').length
+ )
+ range.setStart(node, from || 0)
+ return range
+}
+
+function singleRect(object: Range | Element, bias: number) {
+ const rects = object.getClientRects()
+ return !rects.length
+ ? object.getBoundingClientRect()
+ : rects[bias < 0 ? 0 : rects.length - 1]
+}
+
+function coordsAtPos(view: EditorView, pos: number, end = false) {
+ const { node, offset } = view.domAtPos(pos) //view.docView.domFromPos(pos)
+ let side: DOMRectSide | null = null
+ let rect: DOMRect | null = null
+ if (node.nodeType === 3) {
+ const nodeValue = node.nodeValue || '
+ if (end && offset < nodeValue.length) {
+ rect = singleRect(textRange(node, offset - 1, offset), -1)
+ side = 'right'
+ } else if (offset < nodeValue.length) {
+ rect = singleRect(textRange(node, offset, offset + 1), -1)
+ side = 'left'
+ }
+ } else if (node.firstChild) {
+ if (offset < node.childNodes.length) {
+ const child = node.childNodes[offset]
+ rect = singleRect(
+ child.nodeType === 3 ? textRange(child) : (child as Element),
+ -1
+ )
+ side = 'left'
+ }
+ if ((!rect || rect.top === rect.bottom) && offset) {
+ const child = node.childNodes[offset - 1]
+ rect = singleRect(
+ child.nodeType === 3 ? textRange(child) : (child as Element),
+ 1
+ )
+ side = 'right'
+ }
+ } else {
+ const element = node as Element
+ rect = element.getBoundingClientRect()
+ side = 'left'
+ }
+
+ if (rect && side) {
+ const x = rect[side]
+
+ return {
+ top: rect.top,
+ bottom: rect.bottom,
+ left: x,
+ right: x,
+ }
+ }
+ return {
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ }
+}
+
+class Menu {
+ public options: BubbleMenuPluginOptions
+ public editorView: EditorView
+ public isActive = false
+ public left = 0
+ public bottom = 0
+ public top = 0
+ public preventHide = false
+
+ constructor({
+ options,
+ editorView,
+ }: {
+ options: BubbleMenuPluginOptions
+ editorView: EditorView
+ }) {
+ this.options = {
+ ...{
+ element: null,
+ keepInBounds: true,
+ onUpdate: () => false,
+ },
+ ...options,
+ }
+ this.editorView = editorView
+ this.options.element.addEventListener('mousedown', this.mousedownHandler, {
+ capture: true,
+ })
+ this.options.editor.on('focus', this.focusHandler)
+ this.options.editor.on('blur', this.blurHandler)
+ }
+
+ mousedownHandler = () => {
+ this.preventHide = true
+ }
+ focusHandler = () => {
+ this.update(this.options.editor.view)
+ }
+ blurHandler = ({ event }: { event: FocusEvent }) => {
+ if (this.preventHide) {
+ this.preventHide = false
+ return
+ }
+
+ this.hide(event)
+ }
+
+ update(view: EditorView, lastState?: EditorState) {
+ const { state } = view
+
+ if (view.composing) {
+ return
+ }
+
+ if (
+ lastState &&
+ lastState.doc.eq(state.doc) &&
+ lastState.selection.eq(state.selection)
+ ) {
+ return
+ }
+
+ if (state.selection.empty) {
+ this.hide()
+ return
+ }
+
+ const { from, to } = state.selection
+ const start = coordsAtPos(view, from)
+ const end = coordsAtPos(view, to, true)
+ const parent = this.options.element.offsetParent
+
+ if (!parent) {
+ this.hide()
+ return
+ }
+
+ const box = parent.getBoundingClientRect()
+ const el = this.options.element.getBoundingClientRect()
+ const left = (start.left + end.left) / 2 - box.left
+
+ this.left = Math.round(
+ this.options.keepInBounds
+ ? Math.min(box.width - el.width / 2, Math.max(left, el.width / 2))
+ : left
+ )
+ this.bottom = Math.round(box.bottom - start.top)
+ this.top = Math.round(end.bottom - box.top)
+ this.isActive = true
+
+ this.sendUpdate()
+ }
+
+ sendUpdate() {
+ this.options.onUpdate({
+ isActive: this.isActive,
+ left: this.left,
+ bottom: this.bottom,
+ top: this.top,
+ })
+ }
+
+ hide(event?: FocusEvent) {
+ if (
+ event &&
+ event.relatedTarget &&
+ this.options.element.parentNode &&
+ this.options.element.parentNode.contains(event.relatedTarget as Node)
+ ) {
+ return
+ }
+
+ this.isActive = false
+ this.sendUpdate()
+ }
+
+ destroy() {
+ this.options.element.removeEventListener(
+ 'mousedown',
+ this.mousedownHandler
+ )
+ this.options.editor.off('focus', this.focusHandler)
+ this.options.editor.off('blur', this.blurHandler)
+ }
+}
+
+const BubbleMenu = (options: BubbleMenuPluginOptions) => {
+ return new Plugin({
+ key: new PluginKey('menu_bubble'),
+ view(editorView) {
+ return new Menu({ editorView, options })
+ },
+ })
+}
+
+export { BubbleMenu }
+```
diff --git a/docs/src/docPages/guide/getting-started/vue2.md b/docs/src/docPages/guide/getting-started/vue2.md
new file mode 100644
index 00000000..7f004807
--- /dev/null
+++ b/docs/src/docPages/guide/getting-started/vue2.md
@@ -0,0 +1,104 @@
+# Vue.js 2
+
+## toc
+
+## Introduction
+The following guide describes how to integrate tiptap with your [Vue](https://vuejs.org/) CLI project.
+
+## Requirements
+* [Node](https://nodejs.org/en/download/) installed on your machine
+* [Vue CLI](https://cli.vuejs.org/) installed on your machine
+* Experience with [Vue](https://vuejs.org/v2/guide/#Getting-Started)
+
+## 1. Create a project (optional)
+If you already have an existing Vue project, that’s fine too. Just skip this step and proceed with the next step.
+
+For the sake of this guide, let’s start with a fresh Vue project called `tiptap-example`. The Vue CLI sets up everything we need, just select the default Vue 2 template.
+
+```bash
+# create a project
+vue create tiptap-example
+
+# change directory
+cd tiptap-example
+```
+
+## 2. Install the dependencies
+Okay, enough of the boring boilerplate work. Let’s finally install tiptap! For the following example you’ll need `@tiptap/core` (the actual editor) and the `@tiptap/vue-starter-kit` which has everything to get started quickly, for example a few default extensions and a basic Vue component.
+
+```bash
+# install with npm
+npm install @tiptap/core @tiptap/vue-starter-kit
+
+# install with Yarn
+yarn add @tiptap/core @tiptap/vue-starter-kit
+```
+
+If you followed step 1 and 2, you can now start your project with `npm run dev` or `yarn dev`, and open [http://localhost:8080/](http://localhost:3000/) in your favorite browser. This might be different, if you’re working with an existing project.
+
+## 3. Create a new component
+To actually start using tiptap, you’ll need to add a new component to your app. Let’s call it `Tiptap` and put the following example code in `components/Tiptap.vue`.
+
+This is the fastest way to get tiptap up and running with Vue. It will give you a very basic version of tiptap, without any buttons. No worries, you will be able to add more functionality soon.
+
+```html
+
+
+
+
+
+```
+
+## 4. Add it to your app
+Now, let’s replace the content of `src/App.vue` with the following example code to use our new `Tiptap` component in our app.
+
+```html
+
+
+
+
+
+
+
+```
+
+You should now see tiptap in your browser. You’ve successfully set up tiptap! Time to give yourself a pat on the back. Let’s start to configure your editor in the next step.
+
+## 5. Use v-model (optional)
+You’re probably used to bind your data with `v-model` in forms, that’s also possible with tiptap. Here is a working example component, that you can integrate in your project:
+
+
diff --git a/docs/src/links.yaml b/docs/src/links.yaml
index 69faee28..33cbe0f3 100644
--- a/docs/src/links.yaml
+++ b/docs/src/links.yaml
@@ -50,7 +50,11 @@
link: /guide/getting-started
items:
- title: Vue.js 2
+ link: /guide/getting-started/vue2
+ skip: true
+ - title: Vue.js 3
link: /guide/getting-started/vue
+ type: draft
skip: true
- title: Nuxt.js
link: /guide/getting-started/nuxt