React developers often need to work with several DOM elements at the same time. For example, you might want to focus a group of buttons, attach an event listener to several elements, or measure the positions of multiple elements on a page.
One common solution is to place the elements inside a wrapper <div>. However, adding a wrapper is not always desirable. It can affect CSS layouts, introduce unnecessary DOM elements, or cause problems when working with grid and flexbox layouts.
React Fragments help solve the extra-wrapper problem by allowing multiple elements to be grouped without creating an additional DOM node. Fragment refs extend this idea by providing a way to interact with the DOM elements inside a Fragment as a group.
There is one important detail: Fragment refs are an experimental React feature available in Canary builds, not a generally available feature that every stable React application can use. Before using them, check whether your React version supports the API.
What Is a React Fragment?
A React Fragment allows a component to return multiple elements without adding an extra element to the DOM.
Consider this example:
function UserActions() {
return (
<div>
<button type="button">Save</button>
<button type="button">Cancel</button>
</div>
);
}This component creates a <div> around the two buttons.
The resulting HTML looks like this:
<div>
<button>Save</button>
<button>Cancel</button>
</div>If the wrapper is unnecessary, you can replace it with a Fragment.
function UserActions() {
return (
<>
<button type="button">Save</button>
<button type="button">Cancel</button>
</>
);
}React renders the buttons without adding a wrapper element.
<button>Save</button>
<button>Cancel</button>This is useful when the parent layout expects specific elements as direct children, such as when using CSS Grid or Flexbox.
Short and Long Fragment Syntax
React provides two ways to write a Fragment.
The short syntax uses empty angle brackets:
<>
<h2>Account Settings</h2>
<p>Manage your account details.</p>
</>The long syntax uses the Fragment component:
import { Fragment } from "react";
function AccountSettings() {
return (
<Fragment>
<h2>Account Settings</h2>
<p>Manage your account details.</p>
</Fragment>
);
}Both approaches group elements without creating a wrapper DOM element.
The long syntax is required when you need to provide a key to a Fragment in a list. It is also the syntax used when working with the experimental Fragment ref API.
What Are React Fragment Refs?
A normal React ref provides access to a particular DOM element or another value. For example, you can attach a ref to an input and use it to focus that input.
import { useRef } from "react";
function SearchBox() {
const inputRef = useRef(null);
function focusInput() {
inputRef.current?.focus();
}
return (
<div>
<input ref={inputRef} type="text" />
<button type="button" onClick={focusInput}>
Focus Search
</button>
</div>
);
}This works because the ref points directly to the input element.
A Fragment does not create a DOM element, so it cannot provide a normal element reference. Fragment refs introduce a different approach: in supported experimental React builds, a ref can be attached to a Fragment to access a Fragment instance that provides operations over its child DOM elements.
Instead of managing a separate ref for every button, you can work with the group through the Fragment instance.
The important difference is that the ref does not point to a wrapper element. There is no wrapper element in the DOM.
How to Use a Fragment Ref
The following example demonstrates the experimental API. It assumes you are using a React Canary build and matching React type definitions that support Fragment refs.
Step 1: Import React Hooks and Fragment
Start by importing Fragment, useRef, and useEffect.
import { Fragment, useRef, useEffect } from "react";The useRef Hook stores the reference, while useEffect is used to interact with the DOM after React has committed the elements.
Step 2: Create a Fragment Ref
Create a ref and attach it to the Fragment.
const actionsRef = useRef(null);The ref initially contains null. After React mounts the component, a supported Fragment ref can provide the Fragment instance.
Step 3: Access the Child Elements
Here is a complete example that attaches an event listener to the elements inside the Fragment.
import { Fragment, useRef, useEffect } from "react";
function UserActions() {
const actionsRef = useRef(null);
useEffect(() => {
const fragment = actionsRef.current;
if (!fragment) {
return;
}
function handleClick(event) {
console.log("Clicked element:", event.target);
}
fragment.addEventListener("click", handleClick);
return () => {
fragment.removeEventListener("click", handleClick);
};
}, []);
return (
<Fragment ref={actionsRef}>
<button type="button">Save</button>
<button type="button">Cancel</button>
<button type="button">Delete</button>
</Fragment>
);
}
export default UserActions;In this example, the Fragment instance manages the event listener for its child elements without requiring a wrapper <div>.
The effect also removes the listener during cleanup. This is important because an event listener that is not removed can cause unwanted behavior when a component is unmounted or its effects are restarted.
Remember that this example depends on the experimental Fragment ref API. It will not work in React versions that do not support refs on Fragments.
Working With Focus Across Multiple Elements
One useful application of Fragment refs is managing keyboard focus across a group of elements.
Imagine a dialog containing several buttons. You may want to move focus to the first or last focusable element when a particular action occurs.
With individual refs, you might need to create and maintain a separate ref for every button. A supported Fragment instance can provide group-level focus operations instead.
import { Fragment, useRef } from "react";
function DialogActions() {
const actionsRef = useRef(null);
function focusFirstAction() {
actionsRef.current?.focus();
}
function focusLastAction() {
actionsRef.current?.focusLast();
}
return (
<section>
<h2>Confirm Action</h2>
<Fragment ref={actionsRef}>
<button type="button">Confirm</button>
<button type="button">Cancel</button>
<button type="button">Help</button>
</Fragment>
<div>
<button type="button" onClick={focusFirstAction}>
Focus First Action
</button>
<button type="button" onClick={focusLastAction}>
Focus Last Action
</button>
</div>
</section>
);
}
export default DialogActions;The focus() and focusLast() operations are part of the experimental Fragment instance API. Their behavior depends on the supported elements and the implementation provided by the React version you use.
For production dialogs, you must also consider focus trapping, Escape-key handling, and returning focus to the element that opened the dialog. Focusing the first or last element alone does not implement a complete accessible dialog.
Join the conversation! Your thoughts help the community grow.