From 37a3b840d8c971fd3868cb1c70b56f1c92ab2493 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Wed, 2 Jul 2025 15:04:10 -0400 Subject: [PATCH 1/3] Add experimental docs for Fragment refs --- src/content/reference/react/Fragment.md | 122 +++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index aa72b08a0ba..25d4cd21c7d 100644 --- a/src/content/reference/react/Fragment.md +++ b/src/content/reference/react/Fragment.md @@ -4,7 +4,9 @@ title: (<>...) -``, often used via `<>...` syntax, lets you group elements without a wrapper node. +``, often used via `<>...` syntax, lets you group elements without a wrapper node. + + Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. See reference and usage below. ```js <> @@ -28,6 +30,33 @@ Wrap elements in `` to group them together in situations where you nee #### Props {/*props*/} - **optional** `key`: Fragments declared with the explicit `` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key) +- **optional** `ref`: A ref object or callback function. React will forward the ref to a `FragmentInstance` object that provides methods for interacting with the DOM nodes wrapped by the Fragment. + +### FragmentInstance {/*fragmentinstance*/} + +When you pass a ref to a fragment, React provides a `FragmentInstance` object with methods for interacting with the DOM nodes wrapped by the fragment: + +**Event handling methods:** +- `addEventListener(type, listener, options?)`: Adds an event listener to all first-level DOM children of the Fragment. +- `removeEventListener(type, listener, options?)`: Removes an event listener from all first-level DOM children of the Fragment. +- `dispatchEvent(event)`: Dispatches an event to a virtual child of the Fragment to call any added listeners and can bubble to the DOM parent. + +**Layout methods:** +- `compareDocumentPosition(otherNode)`: Compares the document position of the Fragment with another node. + - If the Fragment has children, the native `compareDocumentPosition` value is returned. + - Empty Fragments will attempt to compare positioning within the React tree and include `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`. + - Elements that have a different relationship in the React tree and DOM tree due to portaling or other insertions are `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`. +- `getClientRects()`: Returns a flat array of `DOMRect` objects representing the bounding rectangles of all children. +- `getRootNode()`: Returns the root node containing the Fragment's parent DOM node. + +**Focus management methods:** +- `focus(options?)`: Focuses the first focusable DOM node in the Fragment. Focus is attempted on nested children depth-first. +- `focusLast(options?)`: Focuses the last focusable DOM node in the Fragment. Focus is attempted on nested children depth-first. +- `blur()`: Removes focus if `document.activeElement` is within the Fragment. + +**Observer methods:** +- `observeUsing(observer)`: Starts observing the Fragment's DOM children with an IntersectionObserver or ResizeObserver. +- `unobserveUsing(observer)`: Stops observing the Fragment's DOM children with the specified observer. #### Caveats {/*caveats*/} @@ -35,6 +64,8 @@ Wrap elements in `` to group them together in situations where you nee - React does not [reset state](/learn/preserving-and-resetting-state) when you go from rendering `<>` to `[]` or back, or when you go from rendering `<>` to `` and back. This only works a single level deep: for example, going from `<><>` to `` resets the state. See the precise semantics [here.](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b) +- If you want to pass `ref` to a Fragment, you can't use the `<>...` syntax. You have to explicitly import `Fragment` from `'react'` and render `...`. + --- ## Usage {/*usage*/} @@ -208,3 +239,92 @@ function PostBody({ body }) { ``` + +--- + +### Using Fragment refs for DOM interaction {/*using-fragment-refs-for-dom-interaction*/} + +Fragment refs allow you to interact with the DOM nodes wrapped by a Fragment without adding extra wrapper elements. This is useful for event handling, visibility tracking, focus management, and replacing deprecated patterns like `ReactDOM.findDOMNode()`. + +```js +import { Fragment } from 'react'; + +function ClickableFragment({ children, onClick }) { + return ( + { + fragmentInstance.addEventListener('click', handleClick); + return () => fragmentInstance.removeEventListener('click', handleClick); + }}> + {children} + + ); +} +``` +--- + +### Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/} + +Fragment refs are useful for visibility tracking and intersection observation. This enables you to monitor when content becomes visible without requiring the child components to expose refs: + +```js {19,21,31-34} +import { Fragment, useRef, useLayoutEffect } from 'react'; + +function VisibilityObserverFragment({ threshold = 0.5, onVisibilityChange, children }) { + const fragmentRef = useRef(null); + + useLayoutEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + onVisibilityChange(entries.some(entry => entry.isIntersecting)) + }, + { threshold } + ); + + fragmentRef.current.observeUsing(observer); + return () => fragmentRef.current.unobserveUsing(observer); + }, [threshold, onVisibilityChange]); + + return ( + + {children} + + ); +} + +function MyComponent() { + const handleVisibilityChange = (isVisible) => { + console.log('Component is', isVisible ? 'visible' : 'hidden'); + }; + + return ( + + + + + ); +} +``` + +This pattern is an alternative to effect-based visibility logging, which is usually not what you want since you don't know of the element you're targeting is in the viewport. + +--- + +### Focus management with Fragment refs {/*focus-management-with-fragment-refs*/} + +Fragment refs provide focus management methods that work across all DOM nodes within the Fragment: + +```js +import { Fragment, useRef } from 'react'; + +function FocusFragment({ children }) { + const fragmentRef = useRef(null); + + return ( + fragmentInstance?.focus()}> + {children} + + ); +} +``` + +The `focus()` method focuses the first focusable element within the Fragment, while `focusLast()` focuses the last focusable element. From 3996b92aae9504c1c4f24b324a6a27e044b92640 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Fri, 26 Sep 2025 17:38:42 -0400 Subject: [PATCH 2/3] Update wording --- src/content/reference/react/Fragment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index 25d4cd21c7d..3c33e2ddaa9 100644 --- a/src/content/reference/react/Fragment.md +++ b/src/content/reference/react/Fragment.md @@ -305,7 +305,7 @@ function MyComponent() { } ``` -This pattern is an alternative to effect-based visibility logging, which is usually not what you want since you don't know of the element you're targeting is in the viewport. +This pattern is an alternative to effect-based visibility logging, which is an anti-pattern in most cases. Relying on effects alone does not guarantee that the rendered component is observable by the user. --- From e37bd7cd46025a4fd2419be27b3ca46c2779ab06 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Fri, 3 Oct 2025 10:31:47 -0400 Subject: [PATCH 3/3] Address feedback --- src/content/reference/react/Fragment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/reference/react/Fragment.md b/src/content/reference/react/Fragment.md index 3c33e2ddaa9..6ad37c832e8 100644 --- a/src/content/reference/react/Fragment.md +++ b/src/content/reference/react/Fragment.md @@ -30,7 +30,7 @@ Wrap elements in `` to group them together in situations where you nee #### Props {/*props*/} - **optional** `key`: Fragments declared with the explicit `` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key) -- **optional** `ref`: A ref object or callback function. React will forward the ref to a `FragmentInstance` object that provides methods for interacting with the DOM nodes wrapped by the Fragment. +- **optional** `ref`: A ref object (e.g. from [`useRef`](/reference/react/useRef)) or [callback function](/reference/react-dom/components/common#ref-callback). React provides a `FragmentInstance` as the ref value that implements methods for interacting with the DOM nodes wrapped by the Fragment. ### FragmentInstance {/*fragmentinstance*/} @@ -264,7 +264,7 @@ function ClickableFragment({ children, onClick }) { ### Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/} -Fragment refs are useful for visibility tracking and intersection observation. This enables you to monitor when content becomes visible without requiring the child components to expose refs: +Fragment refs are useful for visibility tracking and intersection observation. This enables you to monitor when content becomes visible without requiring the child Components to expose refs: ```js {19,21,31-34} import { Fragment, useRef, useLayoutEffect } from 'react'; @@ -305,7 +305,7 @@ function MyComponent() { } ``` -This pattern is an alternative to effect-based visibility logging, which is an anti-pattern in most cases. Relying on effects alone does not guarantee that the rendered component is observable by the user. +This pattern is an alternative to Effect-based visibility logging, which is an anti-pattern in most cases. Relying on Effects alone does not guarantee that the rendered Component is observable by the user. ---