Skip to content

NodeSelector in @firecms/editor caches activeItem on stable editor reference, never updates #826

@moostoet

Description

@moostoet

Package

@firecms/editor@3.1.0 (also reproduces against 3.1.0 HEAD on master)

Bug

Inside the bubble-menu NodeSelector, the indicator (label + check mark) for the current block type is frozen on first render. Selecting different types (Heading 1 → Heading 3 → Bullet List, etc.) does not update the selector. It always reports whatever was active first.

Inline mark selectors (bold/italic/strike) update correctly; only the node selector is broken.

Root cause

In packages/editor/src/selectors/node-selector.tsx the React-Compiler-memoized block keys activeItem on the editor reference:

let t1;
if ($[0] !== editor) {
  t1 = items.filter((item) => item.isActive(editor)).pop() ?? { name: "Multiple" };
  $[0] = editor;
  $[1] = t1;
} else {
  t1 = $[1];
}
const activeItem = t1;

The Tiptap editor instance returned by useCurrentEditor() is stable across selection and document changes, so the cache slot is never invalidated and activeItem remains whatever it was on first render.

Reproduce

  1. Open any markdown field powered by FireCMSEditor.
  2. Type a Heading 1, then a Heading 3 below it (via slash command).
  3. Click into the Heading 1, select text → bubble menu shows "Heading 1" ✓
  4. Click into the Heading 3, select text → bubble menu still shows "Heading 1" ✗

Reproduces on FireCMS 3.x with React 19.

Expected

The selector label and check mark should reflect the block type at the current selection, the same behaviour as the inline mark indicators.

Suggested fix

Compute activeItem outside the memo cache or key the cache on something that actually changes with selection (e.g. editor.state.selection.from plus the active node name).

Minimal patch:

-  let t1;
-  if ($[0] !== editor) {
-    t1 = items.filter((item) => item.isActive(editor)).pop() ?? { name: "Multiple" };
-    $[0] = editor;
-    $[1] = t1;
-  } else {
-    t1 = $[1];
-  }
-  const activeItem = t1;
+  const activeItem = items.filter((item) => item.isActive(editor)).pop() ?? { name: "Multiple" };

Happy to open a PR if helpful.

Workaround

Patched locally via patch-package against the compiled bundle.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions