A React Fragment lets a component return several elements without adding a wrapper element to the DOM. It is written as an empty tag pair (<>...</>) or as the Fragment component, which also accepts a key for lists and, since React 19.3, a ref. Fragments matter most where an extra div would break the HTML or the layout: inside tables, lists, CSS grid and flexbox.
This guide explains the syntax, the cases where a react fragment is actually needed, the key warning many people hit, the new Fragment refs, and what fragments do (and don’t do) for performance. I built every example with React 19.3 and checked the result in a headless browser.
Why Components Need Fragments
A component returns one value, so JSX like return <h1 /><p /> is a syntax error: two elements side by side aren’t a single expression. The classic workaround was to wrap them in a <div>. A Fragment groups them in JSX without producing any element in the page:
function Intro() {
return (
<>
<h1>Hello, world</h1>
<p>This renders as an h1 and a p, with no wrapper.</p>
</>
);
}In the DOM (see how the DOM tree works), the parent element receives the <h1> and <p> directly.
Fragment Syntax: <> vs <Fragment>
| Syntax | Import | Accepts | Use it when |
|---|---|---|---|
<>...</> | None | Nothing | Most of the time |
<Fragment>...</Fragment> | import { Fragment } from 'react' | key and (React 19.3+) ref | Rendering fragments in a list, or attaching a ref |
<React.Fragment> | import React from 'react' | Same as Fragment | Older code; identical behaviour |
Fragments accept no other props: there is nothing in the DOM to put a className, style or onClick on. If you need those, you need a real element.
When a Fragment Is Required (Not Just Tidier)
1. Tables and other strict HTML structures
A <tr> may only contain <td> and <th>. A component that renders table cells therefore can’t wrap them in a <div>:
// 1. Table columns: a wrapper <div> would be invalid inside <tr>
function ColumnsDiv() {
return (
<div>
<td>Ada</td>
<td>Lovelace</td>
</div>
);
}
function ColumnsFragment() {
return (
<>
<td>Ada</td>
<td>Lovelace</td>
</>
);
}
export function TableDemo({ useDiv }) {
return (
<table>
<tbody>
<tr>{useDiv ? <ColumnsDiv /> : <ColumnsFragment />}</tr>
</tbody>
</table>
);
}With the <div> version, the row’s only child was a DIV and React logged “In HTML, <div> cannot be a child of <tr>. This will cause a hydration error.” (plus a matching warning for the <td>s). With the Fragment, the row contained TD, TD and there were no warnings. The same applies to <ul>/<ol> (only <li>), <dl>, <select> and <tbody>.
2. CSS grid and flexbox
Grid and flex layouts apply to direct children. A wrapper turns several items into one:
// 2. Layout: a wrapper becomes one grid item instead of two
function FieldDiv() {
return (
<div>
<label htmlFor="email">Email</label>
<input id="email" />
</div>
);
}
function FieldFragment() {
return (
<>
<label htmlFor="email">Email</label>
<input id="email" />
</>
);
}
export function GridDemo({ useDiv }) {
return (
<form style={{ display: 'grid', gridTemplateColumns: '120px 200px', gap: 8 }}>
{useDiv ? <FieldDiv /> : <FieldFragment />}
</form>
);
}In my test, the form had 1 grid item with the wrapper, and the label sat above the input instead of beside it. With the Fragment it had 2 grid items and they lined up in the two columns as intended.
3. Returning siblings from a component used inside another layout
Any component that will be dropped into a parent’s layout, such as page sections inside a <main> or menu items inside a <nav>, should return its elements without inventing a wrapper the parent’s CSS doesn’t expect.
Fragments in Lists: The key Warning
When you map over data and each item renders several elements, wrap them in a Fragment with a key. The short syntax can’t take a key:
// 3. Lists: keyed Fragment for each pair of <dt>/<dd>
const glossary = [
{ id: 'jsx', term: 'JSX', meaning: 'HTML-like syntax for React elements' },
{ id: 'prop', term: 'Prop', meaning: 'An input passed to a component' },
];
export function Glossary({ withKeys }) {
return (
<dl>
{glossary.map((item) =>
withKeys ? (
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.meaning}</dd>
</Fragment>
) : (
<>
<dt>{item.term}</dt>
<dd>{item.meaning}</dd>
</>
),
)}
</dl>
);
}Both versions produced the same DOM (DT, DD, DT, DD), but the version using <> logged React’s warning: Each child in a list should have a unique “key” prop. The warning appears only in development builds, which is why it can go unnoticed. Use a stable ID from your data as the key, not the array index, so React keeps state on the right item when the list changes (I showed what goes wrong with index keys in advanced React concepts). See also Rendering Lists on react.dev.
Fragment Refs (React 19.3)
Since React 19.3, <Fragment> accepts a ref. Instead of a DOM node, the ref gives you a fragment instance that applies operations to the fragment’s child elements, so you can add event listeners, focus or observe a group of elements without adding a wrapper:
// 4. Fragment refs (React 19.3): one ref for several children, no wrapper
export function FragmentRefDemo() {
const ref = useRef(null);
const [clicks, setClicks] = useState(0);
const [visible, setVisible] = useState([]);
useEffect(() => {
const fragment = ref.current;
const onClick = () => setClicks((c) => c + 1);
fragment.addEventListener('click', onClick); // added to every child element
const observer = new IntersectionObserver((entries) =>
setVisible(entries.filter((e) => e.isIntersecting).map((e) => e.target.textContent)),
);
fragment.observeUsing(observer); // observes every child element
return () => {
fragment.removeEventListener('click', onClick);
fragment.unobserveUsing(observer);
};
}, []);
return (
<section>
<Fragment ref={ref}>
<button>First</button>
<button>Second</button>
</Fragment>
<p id="frag-clicks">Clicks: {clicks}</p>
<p id="frag-visible">Visible: {visible.join(', ')}</p>
</section>
);
}Clicking each button increased the counter (2 clicks counted), and the IntersectionObserver reported both buttons as visible. The fragment instance in React DOM 19.3.1 also has focus(), focusLast(), blur(), getClientRects(), scrollIntoView(), compareDocumentPosition() and dispatchEvent(). Before 19.3 you needed a wrapper element or one ref per child for this.
Do Fragments Improve Performance?
A little, and only at scale. Each wrapper is one more DOM element to create, style and lay out. I rendered a definition list where each row was either wrapped in a <div> or returned as a Fragment, in a production build:
// 5. Many rows: wrapper div vs Fragment
function RowDiv({ i }) { return <div><dt>Term {i}</dt><dd>Value {i}</dd></div>; }
function RowFragment({ i }) { return <><dt>Term {i}</dt><dd>Value {i}</dd></>; }
export function ManyRows({ useDiv, n = 5000 }) {
const Row = useDiv ? RowDiv : RowFragment;
return <dl id="many">{Array.from({ length: n }, (_, i) => <Row key={i} i={i} />)}</dl>;
}| Rows | DOM elements (div / Fragment) | Render and paint (div / Fragment) |
|---|---|---|
| 5,000 | 15,000 / 10,000 | 63–71 ms / 53–65 ms |
| 20,000 | 60,000 / 40,000 | 270–273 ms / 241–246 ms |
That’s roughly 10% faster for a very large list, and nothing you’d notice in a normal component. Choose Fragments for correct HTML and layout; treat the speed-up as a bonus. For genuinely large lists, virtualization matters far more (see how to optimize DOM size in React).
Other Ways to Return Several Elements
// React.createElement, what JSX compiles to
return React.createElement(Fragment, null, <h1>Title</h1>, <p>Text</p>);
// An array also works, but every item needs a key
return [<h1 key="title">Title</h1>, <p key="text">Text</p>];Fragments are clearer than arrays for static content because they need no keys and no commas. Arrays are fine when the elements genuinely come from data.
Fragment vs div: When to Use Which
| Use a Fragment when | Use a real element when |
|---|---|
| The parent is a table, list, grid or flex container | You need a class, style, event handler or ARIA attribute |
| A component returns sibling elements | The group is a meaningful section (<section>, <article>, <nav>) |
You map data to several elements per item (with key) | You need a single box for layout, scrolling or positioning |
Don’t swap a semantic element for a Fragment just to save a node: structure and accessibility matter more than one fewer element. If you’re still learning how components return JSX, start with React core concepts.
Conclusion
Use <> whenever a component needs to return several elements without a wrapper, and <Fragment key={id}> when you render fragments in a list. Fragments are essential inside tables, lists, grids and flex containers, where a stray <div> breaks the HTML or the layout, and since React 19.3 a Fragment ref lets you work with a group of elements without adding one. The performance gain is real but small, so pick Fragments for correctness first.
FAQs
What is a React Fragment?
A built-in component that groups several elements without adding an element to the DOM. Write it as <></> or <Fragment></Fragment>.
What is the difference between <> and <Fragment> in React?
They render the same thing. The short syntax <></> cannot take any attributes, while <Fragment> accepts a key, needed when rendering fragments in a list, and a ref in React 19.3 and later.
When should I use a Fragment instead of a div?
When a wrapper would break the HTML or layout: inside tables, lists, definition lists, CSS grid and flexbox, or whenever a component returns sibling elements. Use a real element when you need classes, styles, events or semantic meaning.
Can a React Fragment have a key?
Yes, with the <Fragment key={id}> syntax. Use it when mapping data to several elements per item; otherwise React warns that each child in a list should have a unique key prop.
Can I add a className or onClick to a Fragment?
No. A Fragment renders no DOM element, so there is nothing to attach them to. In React 19.3 and later you can add a ref and attach event listeners to all its children through the fragment instance.
Do React Fragments improve performance?
Slightly. In my test a 20,000-row list rendered about 10% faster with Fragments than with wrapper divs. For normal components the difference is negligible; use Fragments mainly for correct HTML and layout.








