You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`<Fragment>`, often used via `<>...</>` syntax, lets you group elements without a wrapper node.
8
8
9
-
<Canary>Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements. See reference and usage below.</Canary>
9
+
<Canary>Fragments can also accept refs, which enable interacting with underlying DOM nodes without adding wrapper elements.</Canary>
10
10
11
11
```js
12
12
<>
@@ -30,41 +30,224 @@ Wrap elements in `<Fragment>` to group them together in situations where you nee
30
30
#### Props {/*props*/}
31
31
32
32
-**optional**`key`: Fragments declared with the explicit `<Fragment>` syntax may have [keys.](/learn/rendering-lists#keeping-list-items-in-order-with-key)
33
-
- <CanaryBadge /> **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.
33
+
- <CanaryBadge /> **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.
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:
37
+
* If you want to pass `key`to a Fragment, you can't use the `<>...</>` syntax. You have to explicitly import `Fragment` from `'react'` and render `<Fragment key={yourKey}>...</Fragment>`.
38
38
39
-
**Event handling methods:**
40
-
-`addEventListener(type, listener, options?)`: Adds an event listener to all first-level DOM children of the Fragment.
41
-
-`removeEventListener(type, listener, options?)`: Removes an event listener from all first-level DOM children of the Fragment.
42
-
-`dispatchEvent(event)`: Dispatches an event to a virtual child of the Fragment to call any added listeners and can bubble to the DOM parent.
39
+
* React does not [reset state](/learn/preserving-and-resetting-state) when you go from rendering `<><Child /></>` to `[<Child />]` or back, or when you go from rendering `<><Child /></>` to `<Child />` and back. This only works a single level deep: for example, going from `<><><Child /></></>` to `<Child />` resets the state. See the precise semantics [here.](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b)
43
40
44
-
**Layout methods:**
45
-
-`compareDocumentPosition(otherNode)`: Compares the document position of the Fragment with another node.
46
-
- If the Fragment has children, the native `compareDocumentPosition` value is returned.
47
-
- Empty Fragments will attempt to compare positioning within the React tree and include `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC`.
48
-
- 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`.
49
-
-`getClientRects()`: Returns a flat array of `DOMRect` objects representing the bounding rectangles of all children.
50
-
-`getRootNode()`: Returns the root node containing the Fragment's parent DOM node.
41
+
* <CanaryBadge /> 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 `<Fragment ref={yourRef}>...</Fragment>`.
51
42
52
-
**Focus management methods:**
53
-
-`focus(options?)`: Focuses the first focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.
54
-
-`focusLast(options?)`: Focuses the last focusable DOM node in the Fragment. Focus is attempted on nested children depth-first.
55
-
-`blur()`: Removes focus if `document.activeElement` is within the Fragment.
43
+
---
56
44
57
-
**Observer methods:**
58
-
-`observeUsing(observer)`: Starts observing the Fragment's DOM children with an IntersectionObserver or ResizeObserver.
59
-
-`unobserveUsing(observer)`: Stops observing the Fragment's DOM children with the specified observer.
When you pass a `ref` to a Fragment, React provides a `FragmentInstance` object. It implements methods for interacting with the first-level DOM children wrapped by the Fragment.
48
+
49
+
```js
50
+
import { Fragment, useRef } from'react';
51
+
52
+
functionMyComponent() {
53
+
constref=useRef(null);
54
+
return (
55
+
<Fragment ref={ref}>
56
+
<div id="A"/>
57
+
<Wrapper>
58
+
<div id="B">
59
+
<div id="C"/>
60
+
</div>
61
+
</Wrapper>
62
+
<div id="D"/>
63
+
</Fragment>
64
+
);
65
+
}
66
+
```
67
+
68
+
In the example above, the `FragmentInstance` targets `A`, `B`, and `D`—the first-level host (DOM) children. `C` is not targeted because it is nested inside `B`. If a new host sibling is dynamically added alongside `A`, `B`, or `D`, the `FragmentInstance` automatically tracks it and applies existing event listeners and observers.
*`type`: A string representing the event type to listen for (e.g. `'click'`, `'focus'`).
81
+
*`listener`: The event handler function.
82
+
***optional**`options`: An options object or boolean for capture, matching the [DOM `addEventListener` API.](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
83
+
84
+
##### Returns {/*addeventlistener-returns*/}
85
+
86
+
`addEventListener` does not return anything (`undefined`).
*`listener`: The event handler function to remove.
100
+
***optional**`options`: An options object or boolean, matching the [DOM `removeEventListener` API.](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener)
101
+
102
+
##### Returns {/*removeeventlistener-returns*/}
103
+
104
+
`removeEventListener` does not return anything (`undefined`).
105
+
106
+
#### `dispatchEvent(event)` {/*dispatchevent*/}
107
+
108
+
Dispatches an event on the Fragment. Added event listeners are called, and the event can bubble to the Fragment's DOM parent.
*`event`: An [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event) object to dispatch. If `bubbles` is `true`, the event bubbles to the Fragment's parent DOM node.
117
+
118
+
##### Returns {/*dispatchevent-returns*/}
119
+
120
+
`true` if the event was not cancelled, `false` if `preventDefault()` was called.
121
+
122
+
#### `focus(options?)` {/*focus*/}
123
+
124
+
Focuses the first focusable DOM node in the Fragment. Unlike calling `element.focus()` on a DOM element, this method searches *all* nested children depth-first until it finds a focusable element—not just the element itself or its direct children.
125
+
126
+
```js
127
+
fragmentRef.current.focus();
128
+
```
129
+
130
+
##### Parameters {/*focus-parameters*/}
131
+
132
+
***optional**`options`: A [`FocusOptions`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#options) object (e.g. `{ preventScroll: true }`).
133
+
134
+
##### Returns {/*focus-returns*/}
135
+
136
+
`focus` does not return anything (`undefined`).
137
+
138
+
#### `focusLast(options?)` {/*focuslast*/}
139
+
140
+
Focuses the last focusable DOM node in the Fragment. Searches nested children depth-first, then iterates in reverse.
141
+
142
+
```js
143
+
fragmentRef.current.focusLast();
144
+
```
145
+
146
+
##### Parameters {/*focuslast-parameters*/}
147
+
148
+
***optional**`options`: A [`FocusOptions`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#options) object.
149
+
150
+
##### Returns {/*focuslast-returns*/}
151
+
152
+
`focusLast` does not return anything (`undefined`).
153
+
154
+
#### `blur()` {/*blur*/}
155
+
156
+
Removes focus from the active element if it is within the Fragment. If `document.activeElement` is not within the Fragment, `blur` does nothing.
157
+
158
+
```js
159
+
fragmentRef.current.blur();
160
+
```
161
+
162
+
##### Returns {/*blur-returns*/}
163
+
164
+
`blur` does not return anything (`undefined`).
165
+
166
+
#### `observeUsing(observer)` {/*observeusing*/}
167
+
168
+
Starts observing all first-level DOM children of the Fragment with the provided observer.
62
169
63
-
- If you want to pass `key` to a Fragment, you can't use the `<>...</>` syntax. You have to explicitly import `Fragment` from `'react'` and render `<Fragment key={yourKey}>...</Fragment>`.
*`observer`: An [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) or [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) instance.
178
+
179
+
##### Returns {/*observeusing-returns*/}
180
+
181
+
`observeUsing` does not return anything (`undefined`).
Stops observing the Fragment's DOM children with the specified observer.
186
+
187
+
```js
188
+
fragmentRef.current.unobserveUsing(observer);
189
+
```
190
+
191
+
##### Parameters {/*unobserveusing-parameters*/}
192
+
193
+
*`observer`: The same `IntersectionObserver` or `ResizeObserver` instance previously passed to [`observeUsing`](#observeusing).
194
+
195
+
##### Returns {/*unobserveusing-returns*/}
196
+
197
+
`unobserveUsing` does not return anything (`undefined`).
198
+
199
+
#### `getClientRects()` {/*getclientrects*/}
200
+
201
+
Returns a flat array of [`DOMRect`](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) objects representing the bounding rectangles of all first-level DOM children.
202
+
203
+
```js
204
+
constrects=fragmentRef.current.getClientRects();
205
+
```
206
+
207
+
##### Returns {/*getclientrects-returns*/}
208
+
209
+
An `Array<DOMRect>` containing the bounding rectangles of all children.
210
+
211
+
#### `getRootNode(options?)` {/*getrootnode*/}
212
+
213
+
Returns the root node containing the Fragment's parent DOM node, matching the behavior of [`Node.getRootNode()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode).
214
+
215
+
```js
216
+
constroot=fragmentRef.current.getRootNode();
217
+
```
218
+
219
+
##### Parameters {/*getrootnode-parameters*/}
220
+
221
+
***optional**`options`: An object with a `composed` boolean property, matching the [DOM `getRootNode` API.](https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode#options)
222
+
223
+
##### Returns {/*getrootnode-returns*/}
224
+
225
+
A `Document`, `ShadowRoot`, or the `FragmentInstance` itself if there is no parent DOM node.
Compares the document position of the Fragment with another node, returning a bitmask matching the behavior of [`Node.compareDocumentPosition()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition).
- React does not [reset state](/learn/preserving-and-resetting-state) when you go from rendering `<><Child /></>` to `[<Child />]` or back, or when you go from rendering `<><Child /></>` to `<Child />`and back. This only works a single level deep: for example, going from `<><><Child /></></>` to `<Child />` resets the state. See the precise semantics [here.](https://gist.github.com/clemmy/b3ef00f9507909429d8aa0d3ee4f986b)
241
+
A bitmask of [position flags](https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition#return_value). Empty Fragments and Fragments with children rendered through a [portal](/reference/react-dom/createPortal) include `Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC` in the result.
66
242
67
-
- <CanaryBadge /> 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 `<Fragment ref={yourRef}>...</Fragment>`.
* Methods that target children (such as `addEventListener`, `observeUsing`, and `getClientRects`) operate on *first-level host (DOM) children* of the Fragment. They do not directly target children nested inside another DOM element.
246
+
* When host children are dynamically added or removed, the `FragmentInstance` automatically updates. New children receive any previously added event listeners and active observers.
247
+
*`focus` and `focusLast` search nested children depth-first for focusable elements, unlike event and observer methods which only target first-level host children.
248
+
*`observeUsing` does not work on text nodes. React logs a warning in development if the Fragment contains only text children.
249
+
*`focus`, `focusLast`, and `blur` have no effect when the Fragment contains only text children.
250
+
* React does not apply event listeners added via `addEventListener` to hidden [`<Activity>`](/reference/react/Activity) trees. When an `Activity` boundary switches from hidden to visible, listeners are applied automatically.
68
251
69
252
---
70
253
@@ -242,34 +425,61 @@ function PostBody({ body }) {
242
425
243
426
---
244
427
245
-
### <CanaryBadge /> Using Fragment refs for DOM interaction {/*using-fragment-refs-for-dom-interaction*/}
428
+
### <CanaryBadge /> Adding event listeners without a wrapper element {/*adding-event-listeners-without-wrapper*/}
246
429
247
-
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()`.
430
+
Fragment `ref`s let you add event listeners to a group of elements without adding a wrapper DOM node. Use a [ref callback](/reference/react-dom/components/common#ref-callback) to attach and clean up listeners:
The `addEventListener` call applies the listener to every first-level DOM child of the Fragment. When children are dynamically added or removed, the `FragmentInstance` automatically adds or removes the listener.
448
+
449
+
<DeepDive>
450
+
451
+
#### Which children does a Fragment ref target? {/*which-children-does-a-fragment-ref-target*/}
452
+
453
+
A `FragmentInstance` targets the **first-level host (DOM) children** of the Fragment. Consider this tree:
454
+
455
+
```js
456
+
<Fragment ref={ref}>
457
+
<div id="A"/>
458
+
<Wrapper>
459
+
<div id="B">
460
+
<div id="C"/>
461
+
</div>
462
+
</Wrapper>
463
+
<div id="D"/>
464
+
</Fragment>
465
+
```
466
+
467
+
`Wrapper` is a React component, so the `FragmentInstance` looks through it to find DOM nodes. The targeted children are `A`, `B`, and `D`. `C` is not targeted because it is nested inside the DOM element `B`.
468
+
469
+
Methods like `addEventListener`, `observeUsing`, and `getClientRects` operate on these first-level DOM children. `focus` and `focusLast` are different—they search *all* nested children depth-first to find focusable elements.
470
+
471
+
</DeepDive>
472
+
263
473
---
264
474
265
-
### <CanaryBadge /> Tracking visibility with Fragment refs {/*tracking-visibility-with-fragment-refs*/}
475
+
### <CanaryBadge /> Observing visibility without a wrapper element {/*observing-visibility-without-wrapper*/}
266
476
267
-
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:
477
+
Use `observeUsing` to attach an `IntersectionObserver` to all first-level DOM children of a Fragment. This lets you track visibility without requiring child components to expose `ref`s or adding a wrapper element:
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.
309
-
310
505
---
311
506
312
-
### <CanaryBadge /> Focus management with Fragment refs {/*focus-management-with-fragment-refs*/}
507
+
### <CanaryBadge /> Managing focus across a group of elements {/*managing-focus-across-elements*/}
313
508
314
-
Fragment refs provide focus management methods that work across all DOM nodes within the Fragment:
509
+
Fragment `ref`s provide `focus`, `focusLast`, and `blur`methods that operate across all DOM nodes within the Fragment:
<button onClick={focusFirst}>Focus first field</button>
522
+
<Fragment ref={fragmentRef}>
523
+
{children}
524
+
</Fragment>
525
+
</>
324
526
);
325
527
}
326
528
```
327
529
328
-
The `focus()`method focuses the first focusable element within the Fragment, while `focusLast()`focuses the last focusable element.
530
+
`focus()`finds the first focusable element by searching depth-first through all nested children. `focusLast()`does the same in reverse. `blur()` removes focus if the currently focused element is within the Fragment.
0 commit comments