Fragment Refs

A ref needs a node, and a component that returns several siblings has no single node to give. The old fix was a wrapper <div> added purely so something could be measured or observed. React 19.3 7,897 , released on 9 September 2026, removed the need for it: Fragment now accepts a ref and fills it with a fragment instance, an object that forwards a small set of operations to the children it wraps.

A Fragment ref measures and focuses a group of fieldsHTML
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@8.0.5/babel.min.js"></script>
<script type="importmap">{"imports":{"react":"https://esm.sh/react@19.3.0?dev",
"react-dom/client":"https://esm.sh/react-dom@19.3.0/client?dev"}}</script>
<div id="root"></div><script type="text/babel" data-type="module">
  import React, { Fragment, useRef, useState } from 'react';
  import { createRoot } from 'react-dom/client';
  function Fields({ children }) {
    const group = useRef(null), [info, setInfo] = useState('');
    function inspect() {
      const rects = group.current.getClientRects();
      group.current.focus();                       // first focusable child, depth-first
      setInfo(`${rects.length} children, first ${Math.round(rects[0].width)}px wide; `
        + `focus is on #${document.activeElement.id}`);
    }
    return <div style={{font: '15px system-ui'}}>
      <Fragment ref={group}>{children}</Fragment>
      <p><button onClick={inspect}>Inspect the group</button></p>
      <p><b>{info}</b></p></div>;
  }
  createRoot(document.getElementById('root')).render(<Fields>
    <input id="first" placeholder="first" /><input id="last" placeholder="last" /></Fields>);
  setTimeout(() => document.querySelector('button').click(), 400);
</script>
Browser output of Listing 2.45
Browser output of 45

The readout comes entirely from the fragment instance: getClientRects() returns one DOMRect per first-level DOM child, and focus() walks the children depth-first to the first focusable element. The same object offers focusLast(), blur(), addEventListener and removeEventListener (attached to every first-level child), observeUsing and unobserveUsing for an IntersectionObserver or ResizeObserver, dispatchEvent, scrollIntoView, getRootNode and compareDocumentPosition.

Generic wrappers become possible without touching the markup they wrap: a <Highlight> that watches whatever you put inside it for visibility no longer has to impose a <div> that might break a grid or a flex layout.