[React] FloatingFocusManager inserts fallback span into contenteditable even with returnFocus=false
Describe the bug
FloatingFocusManager inserts an empty, visually hidden return-focus fallback <span> immediately after its DOM reference. When that reference is a descendant of a contenteditable root, the fallback becomes part of the editable document.
This also happens with returnFocus={false}, initialFocus={-1}, modal={false}, guards={false}, and preserveTabOrder={false} on FloatingPortal.
In schema-managed editors, this extra child can trigger DOM reconciliation, replace the reference subtree and briefly move code/note/table toolbars to the viewport origin. The reproduction deliberately uses only React and Floating UI, with a native MutationObserver to show the unexpected child insertion; it does not include an editor library or simulate editor reconciliation.
To Reproduce
CodeSandbox: https://codesandbox.io/p/sandbox/nhgj97
Preview: https://nhgj97.csb.app/
Steps to reproduce the behavior:
- Open the preview with
@floating-ui/[email protected](confirm “Yes, proceed to preview” if CodeSandbox asks). - Observe
Unexpected editable child insertions: 0. - Select text inside the editable code block (a click also opens the menu).
- The floating menu opens outside the editor in a portal. The counter becomes
1, and the output shows this new child inside the editable root:
<span tabindex="-1" aria-hidden="true" style="... visually hidden styles ..."></span>The observer is read-only; it only reports added elements. The app does not insert this span.
Expected behavior
Opening a portaled floating menu anchored to content inside an editable document should not insert its return-focus fallback into that document. In particular, disabling return-focus should allow this extra child insertion to be avoided, or there should be a supported way to place the fallback outside the editable root while retaining normal menu focus behavior.
Screenshots
The reproduction displays the exact inserted element and a mutation counter, so no screenshot is needed to observe the problem.
Context:
- OS: macOS 26.3.1
- Browser: Google Chrome 152.0.7977.76
- Version:
@floating-ui/react0.27.20; React / React DOM 18.2.0 - The same insertion also exists in 0.27.19.
Additional context
The insertion is in FloatingFocusManager:
const fallbackEl = doc.createElement('span');
fallbackEl.setAttribute('tabindex', '-1');
fallbackEl.setAttribute('aria-hidden', 'true');
Object.assign(fallbackEl.style, HIDDEN_STYLES);
if (isInsidePortal && domReference) {
domReference.insertAdjacentElement('afterend', fallbackEl);
}A local workaround is to place this fallback after the editable root rather than after its child reference. Ordinary references outside contenteditable retain their existing behavior.
I checked the existing reports:
- #3202 describes a related ProseMirror reconciliation problem, but concerns
data-floating-ui-inert/aria-hiddenattributes.getInsideElementsdoes not control this fallback child insertion. - Discussion #2868 concerns additional portal focus guards. This reproduction disables those guards and still observes the separate return-focus fallback span.
Source: floating-ui/floating-ui