facebook / react · Issue No. 37606
FragmentInstance.compareDocumentPosition reports DOCUMENT_POSITION_DISCONNECTED for the Fragment own children when the Fragment is portaled into a ShadowRoot or DocumentFragment.
Current react/react main: 019019be403c3269e15b8d7ebefb57d30f84086b (React source version 19.3.0). Verified on source, not inferred from an older published release.
The snippet assumes React/createPortal, createRoot and flushSync imports, and a mounted root created with createRoot(container).
const target = document.createElement('div').attachShadow({mode: 'open'});
const ref = React.createRef();
flushSync(() => root.render(createPortal(
<React.Fragment ref={ref}><button /></React.Fragment>, target,
)));
console.log(ref.current.compareDocumentPosition(target.firstChild));
// 1 (DISCONNECTED), expected 16 (CONTAINED_BY).
// document.createDocumentFragment() as target reproduces the same result.
A runnable repository regression test is included in the linked fix PR.
All such children are reported disconnected even though both the DOM and Fiber trees contain them.
Own children and their descendants should be recognized as contained, as they are for an Element portal target.
The portal path derives its DOM parent from firstNode.parentElement. ShadowRoot and DocumentFragment are valid portal containers but are not Elements, so this is null and the method returns DISCONNECTED before checking actual containment.
Source: packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js.
Severity assessment: Medium. FragmentInstance.compareDocumentPosition reports DOCUMENT_POSITION_DISCONNECTED for the Fragment own children when the Fragment is portaled into a ShadowRoot or DocumentFragment.
Before: both ShadowRoot and DocumentFragment cases return 1 instead of 16. After: development and production Fragment suites pass 101/101 tests, with Element portal controls and existing Document/empty-Fragment coverage. Prettier, ESLint and Flow pass.
Searched all Issue/PR states for Fragment parentElement, ShadowRoot, DocumentFragment and compareDocumentPosition. Inspected #37142: it fixes Fiber ancestor validation after imperative DOM movement, not the parentElement null short-circuit. #37163/#37579 address Document/empty-portal paths. No equivalent non-element portal-parent fix was found.
Element parents still produce the same node. parentNode additionally admits supported non-element portal containers; the existing Fiber validation remains in place.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.