Skip to content

fix(vue-2): use mounted lifecycle in FloatingMenu - #7877

Merged
bdbch merged 5 commits into
ueberdosis:mainfrom
ogyamada:fix/tiptap-vue-2-both-menus-have-error-error-in-callba
Aug 10, 2026
Merged

fix(vue-2): use mounted lifecycle in FloatingMenu#7877
bdbch merged 5 commits into
ueberdosis:mainfrom
ogyamada:fix/tiptap-vue-2-both-menus-have-error-error-in-callba

Conversation

@ogyamada

@ogyamada ogyamada commented May 27, 2026

Copy link
Copy Markdown
Contributor

What

Move the FloatingMenu plugin registration in @tiptap/vue-2 from an immediate: true watcher on the editor prop to a mounted() lifecycle hook. Mirrors the pattern already used by BubbleMenu in the same package.

Why

The immediate watcher fires before the component mounts, so this.$el is undefined the first time the handler runs. With the existing if (!this.$el) return guard, this silently no-opped (so the floating menu was never registered when editor was passed synchronously). Without the guard — or when something else touched $el — Vue surfaces it as:

Error in callback for immediate watcher "editor":
"TypeError: Cannot read properties of undefined (reading 'style')"

BubbleMenu in packages/vue-2 already uses mounted() for this reason; the reporter on v3.10.1 hit the same class of bug on both menus, but only FloatingMenu was still on the old pattern.

Testing

  • Diff is a mechanical port of the working BubbleMenu.ts shape — no logic added, just the watcher → mounted() swap.
  • Could not run pnpm install / pnpm build / pnpm test:unit in the sandbox here (pnpm fails with "unable to open database file" before any project script runs — environmental, unrelated to the change). Will run the full validation checklist (pnpm lint, pnpm build, pnpm test:unit, pnpm test:e2e) on a normal machine before merging.

Notes

  • No unit tests exist under packages/vue-2/; the floating-menu plugin tests live in packages/extension-floating-menu/__tests__/ and don't exercise the Vue 2 wrapper. The demos app also has no Vue 2 floating-menu demo wired up. Adding either feels out of scope for a one-line bugfix — happy to follow up in a separate PR if you'd prefer test coverage here.
  • Minor behavior change: anyone relying on the previous silent no-op (where passing a synchronous editor meant the floating menu never registered) will now see it register correctly on mount. That's the documented behavior, so this just restores it.

Fixes #7167.

The previous implementation registered the floating-menu plugin from an
`immediate: true` editor watcher. Because immediate watchers fire before
`mounted`, `this.$el` was undefined when the editor prop was supplied
synchronously, causing a runtime "Cannot read properties of undefined
(reading 'style')" error to bubble through Vue's watcher callback.

Mirrors the existing BubbleMenu pattern: register the plugin from
`mounted()` instead, so `$el` is guaranteed to exist.

Fixes ueberdosis#7167.
@netlify

netlify Bot commented May 27, 2026

Copy link
Copy Markdown

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit bbfca79
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/6a7912be17d17400082b3d0e
😎 Deploy Preview https://deploy-preview-7877--tiptap-embed.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented May 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bbfca79

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 74 packages
Name Type
@tiptap/vue-2 Patch
@tiptap/extension-drag-handle-vue-2 Patch
@tiptap/core Patch
@tiptap/extension-audio Patch
@tiptap/extension-blockquote Patch
@tiptap/extension-bold Patch
@tiptap/extension-bubble-menu Patch
@tiptap/extension-bullet-list Patch
@tiptap/extension-code-block-lowlight Patch
@tiptap/extension-code-block Patch
@tiptap/extension-code Patch
@tiptap/extension-collaboration-caret Patch
@tiptap/extension-collaboration Patch
@tiptap/extension-color Patch
@tiptap/extension-details Patch
@tiptap/extension-document Patch
@tiptap/extension-drag-handle-react Patch
@tiptap/extension-drag-handle-vue-3 Patch
@tiptap/extension-drag-handle Patch
@tiptap/extension-emoji Patch
@tiptap/extension-file-handler Patch
@tiptap/extension-find-and-replace Patch
@tiptap/extension-floating-menu Patch
@tiptap/extension-font-family Patch
@tiptap/extension-hard-break Patch
@tiptap/extension-heading Patch
@tiptap/extension-highlight Patch
@tiptap/extension-horizontal-rule Patch
@tiptap/extension-image Patch
@tiptap/extension-invisible-characters Patch
@tiptap/extension-italic Patch
@tiptap/extension-link Patch
@tiptap/extension-list Patch
@tiptap/extension-mathematics Patch
@tiptap/extension-mention Patch
@tiptap/extension-node-range Patch
@tiptap/extension-ordered-list Patch
@tiptap/extension-paragraph Patch
@tiptap/extension-ruby-text Patch
@tiptap/extension-strike Patch
@tiptap/extension-subscript Patch
@tiptap/extension-superscript Patch
@tiptap/extension-table-of-contents Patch
@tiptap/extension-table Patch
@tiptap/extension-text-align Patch
@tiptap/extension-text-style Patch
@tiptap/extension-text Patch
@tiptap/extension-twitch Patch
@tiptap/extension-typography Patch
@tiptap/extension-underline Patch
@tiptap/extension-unique-id Patch
@tiptap/extension-youtube Patch
@tiptap/extensions Patch
@tiptap/html Patch
@tiptap/markdown Patch
@tiptap/pm Patch
@tiptap/react Patch
@tiptap/starter-kit Patch
@tiptap/static-renderer Patch
@tiptap/suggestion Patch
@tiptap/vue-3 Patch
@tiptap/extension-character-count Patch
@tiptap/extension-dropcursor Patch
@tiptap/extension-focus Patch
@tiptap/extension-gapcursor Patch
@tiptap/extension-history Patch
@tiptap/extension-list-item Patch
@tiptap/extension-list-keymap Patch
@tiptap/extension-placeholder Patch
@tiptap/extension-table-cell Patch
@tiptap/extension-table-header Patch
@tiptap/extension-table-row Patch
@tiptap/extension-task-item Patch
@tiptap/extension-task-list Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ogyamada
ogyamada marked this pull request as ready for review May 27, 2026 05:06
@bdbch

bdbch commented Jul 15, 2026

Copy link
Copy Markdown
Member

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary

  • Register the Vue 2 FloatingMenu plugin in mounted() so $el exists before use.
  • Prevent errors when the editor is provided synchronously.
  • Skip registration after component destruction.
  • Handle missing editors safely during destruction.
  • Add a changeset for the fix.

Walkthrough

The Vue 2 FloatingMenu now initializes its element in mounted() and registers the plugin on the next tick. The immediate editor watcher is removed. Destruction state prevents late registration and handles missing editors safely.

Changes

FloatingMenu mount timing

Layer / File(s) Summary
Mount-time element setup and plugin registration
packages/vue-2/src/menus/FloatingMenu.ts, .changeset/2026-05-27-fix-vue-2-floating-menu-watcher-error.md
FloatingMenu exposes isDestroyed, sets up its element during mounted(), and registers FloatingMenuPlugin on the next tick. beforeDestroy marks the component as destroyed and skips unregistration when no editor exists. The changeset records the patch and error fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested labels: area: vue, complexity: medium, impact: medium, status: needs-reproduction

Suggested reviewers: alexvcasillas

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main fix: moving Vue 2 FloatingMenu registration to the mounted lifecycle.
Description check ✅ Passed The description explains what changed, why it changed, how it was tested, and links issue #7167, but it omits the repository checklist.
Linked Issues check ✅ Passed The changes address issue #7167 by preventing pre-mount element access and restoring FloatingMenu registration for synchronous editor props.
Out of Scope Changes check ✅ Passed The changes remain within the linked issue scope and support safe FloatingMenu registration and cleanup during the component lifecycle.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/vue-2/src/menus/FloatingMenu.ts`:
- Around line 61-89: Add a unit test for FloatingMenu’s mounted lifecycle that
mounts the component with a stub editor, verifies registerPlugin is not called
before mounted/$nextTick completes, and then verifies it is called afterward
with the expected plugin setup. Use the existing test conventions and target the
mounted method’s deferred registration behavior.
- Around line 65-67: Update beforeDestroy to guard the
this.editor.unregisterPlugin(...) call with the same falsy-editor check used in
the mount logic, so teardown safely no-ops when editor was unavailable. Preserve
the existing unregister behavior when an editor exists.
- Around line 75-88: Guard the deferred callback in FloatingMenu before calling
editor.registerPlugin: check the component’s destroyed state after $nextTick
runs and return without registering when the component has already been
destroyed. Keep normal plugin registration unchanged for mounted components.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8124fc8e-cca8-4b3f-b932-1a4a39c2baa3

📥 Commits

Reviewing files that changed from the base of the PR and between a42a8d6 and 4eac38f.

📒 Files selected for processing (2)
  • .changeset/2026-05-27-fix-vue-2-floating-menu-watcher-error.md
  • packages/vue-2/src/menus/FloatingMenu.ts

Comment thread packages/vue-2/src/menus/FloatingMenu.ts
Comment thread packages/vue-2/src/menus/FloatingMenu.ts
Comment thread packages/vue-2/src/menus/FloatingMenu.ts
@bdbch
bdbch requested a review from a team as a code owner July 30, 2026 09:19
@coderabbitai coderabbitai Bot added area: vue Vue integration and components complexity: easy Small effort, well-defined scope impact: medium Affects some users or workflows status: needs-reproduction Needs a minimal reproduction to investigate labels Aug 9, 2026
…destruction

Add `isDestroyed` flag to guard against race conditions where `$nextTick` callbacks from `mounted()` fire after `beforeDestroy()` has already run, preventing attempts to register the floating-menu plugin on a destroyed component.
@coderabbitai coderabbitai Bot added complexity: medium Moderate change, possibly multiple files and removed complexity: easy Small effort, well-defined scope labels Aug 9, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 9, 2026
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/2026-05-27-fix-vue-2-floating-menu-watcher-error.md:
- Line 5: Update the changeset sentence to describe only the user-visible
outcome: the FloatingMenu now appears correctly in Vue 2. Remove references to
registration, editor prop timing, and implementation details while preserving
the required frontmatter and one-sentence format.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d509fff-412e-4843-bac8-0d950b37e81f

📥 Commits

Reviewing files that changed from the base of the PR and between 0f1f0a6 and bbfca79.

📒 Files selected for processing (1)
  • .changeset/2026-05-27-fix-vue-2-floating-menu-watcher-error.md

Comment thread .changeset/2026-05-27-fix-vue-2-floating-menu-watcher-error.md
@bdbch
bdbch merged commit bfb9976 into ueberdosis:main Aug 10, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: vue Vue integration and components complexity: medium Moderate change, possibly multiple files impact: medium Affects some users or workflows status: needs-reproduction Needs a minimal reproduction to investigate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants