ueberdosis/tiptap

Tiptap/vue-2 both menus have error: Error in callback for immediate watcher "editor": "TypeError: Cannot read properties of undefined (reading 'style')"

Open

#7167 opened on Nov 4, 2025

View on GitHub
 (0 comments) (1 reaction) (0 assignees)TypeScript (23,454 stars) (1,979 forks)batch import
area: editorarea: ui-componentsgood first issuestatus: triagetype: help-wanted

Description

Affected Packages

@tiptap/vue-2

Version(s)

3.10.1

Bug Description

The menus are always visible and don't change there positions with user interaction.

Bubble menu and floating menu in tiptap/vue-2 package can't read style from this.$el in watcher because at the time of execution element is undefined. It helps to add setTimeout in watcher but there is probably better solution.

Browser Used

Chrome

Code Example URL

https://codesandbox.io/p/sandbox/quirky-mccarthy-9qjprq

Expected Behavior

Menus from tiptap/vue-2 package to work right out of the box without errors.

Additional Context (Optional)

Solution I made in my project to handle this bug for now .

//Bubble Menu
<template>
  <div ref="customBubbleMenu">
    <slot />
  </div>
</template>

<script>
import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';

export default {
  name: 'BubbleMenu',

  props: {
    pluginKey: {
      type: [String, Object],
      default: 'bubbleMenu',
    },
    editor: {
      type: Object,
      required: true,
    },
    updateDelay: {
      type: Number,
      default: 250,
    },
    options: {
      type: Object,
      default: () => {},
    },
    resizeDelay: {
      type: Number,
      default: 100,
    },
    appendTo: {
      type: [Object, Function],
      default: undefined,
    },
    shouldShow: {
      type: Function,
      default: null,
    },
    getReferencedVirtualElement: {
      type: Function,
      default: null,
    },
  },

  mounted() {
    this.$refs.customBubbleMenu.style.visibility = 'hidden';
    this.$refs.customBubbleMenu.style.position = 'absolute';

    this.$refs.customBubbleMenu.remove();

    this.$nextTick(() => {
      this.editor.registerPlugin(
        BubbleMenuPlugin({
          updateDelay: this.updateDelay,
          resizeDelay: this.resizeDelay,
          options: this.options,
          editor: this.editor,
          element: this.$refs.customBubbleMenu,
          pluginKey: this.pluginKey,
          appendTo: this.appendTo,
          shouldShow: this.shouldShow,
          getReferencedVirtualElement: this.getReferencedVirtualElement,
        })
      );
    });
  },

  beforeDestroy() {
    this.editor.unregisterPlugin(this.pluginKey);
  },
};
</script>

//Floating Menu

<template>
  <div ref="customFloatingMenu">
    <slot />
  </div>
</template>

<script>
import { FloatingMenuPlugin } from '@tiptap/extension-floating-menu';

export default {
  name: 'CustomFloatingMenu',

  props: {
    pluginKey: {
      type: [String, Object],
      default: 'bubbleMenu',
    },
    editor: {
      type: Object,
      required: true,
    },
    options: {
      type: Object,
      default: () => {},
    },
    appendTo: {
      type: [Object, Function],
      default: undefined,
    },
    shouldShow: {
      type: Function,
      default: null,
    },
  },

  mounted() {
    this.$refs.customFloatingMenu.style.visibility = 'hidden';
    this.$refs.customFloatingMenu.style.position = 'absolute';

    this.$refs.customFloatingMenu.remove();

    this.$nextTick(() => {
      this.editor.registerPlugin(
        FloatingMenuPlugin({
          editor: this.editor,
          element: this.$refs.customFloatingMenu,
          pluginKey: this.pluginKey,
          options: this.options,
          appendTo: this.appendTo,
          shouldShow: this.shouldShow,
        })
      );
    });
  },

  beforeDestroy() {
    this.editor.unregisterPlugin(this.pluginKey);
  },
};
</script>

Dependency Updates

  • Yes, I've updated all my dependencies.

Contributor guide