React Fragments and When You Actually Need Them
Why React components must return a single root element, how Fragments solve this without polluting the DOM, and the three ways to write them.
When I first started writing React, I kept bumping into a weird error: "JSX expressions must have one parent element." I would wrap everything in a <div>, the error would go away, and I would move on. It took longer than I want to admit before I realised what that extra <div> was doing to my layouts.
In JSX, every component must return a single root element. That constraint is not arbitrary; it comes from how React constructs the element tree. But adding a <div> wrapper every time you need to return two things side by side adds noise to the DOM. Fragments let you satisfy the single-root rule without leaving a wrapping element behind.
The three ways to write a Fragment
There is the explicit import:
import { Fragment } from 'react';
function NameList({ first, last }: { first: string; last: string }) {
return (
<Fragment>
<span>{first}</span>
<span>{last}</span>
</Fragment>
);
}
The short syntax (which is what I use almost everywhere):
function NameList({ first, last }: { first: string; last: string }) {
return (
<>
<span>{first}</span>
<span>{last}</span>
</>
);
}
And the keyed variant, for when you are inside a map or any list context:
function ItemList({ items }: { items: Array<{ id: string; name: string; count: number }> }) {
return (
<dl>
{items.map(item => (
<Fragment key={item.id}>
<dt>{item.name}</dt>
<dd>{item.count}</dd>
</Fragment>
))}
</dl>
);
}
The short syntax can't take a key or any other prop. That is the only time I reach for the full <Fragment> form.
The DOM pollution problem
The real cost of wrapper divs isn't developer annoyance, it is broken layout expectations. Flexbox and grid children are assumed to be direct descendants. When you slip a <div> between a flex container and its items, the layout engine sees that div as the only flex child. Your intended two-column layout becomes a single column with nested content.
Here is an example I hit when building a card component that returns both a heading and a paragraph:
function Card({ title, body }: { title: string; body: string }) {
return (
<div className="card">
<h2>{title}</h2>
<p>{body}</p>
</div>
);
}
The parent flex container sees one child (the card div) and the layout works. But if the component needed to return the heading and paragraph without the card wrapper, because the parent should handle layout, a Fragment keeps things flat:
function CardContent({ title, body }: { title: string; body: string }) {
return (
<>
<h2>{title}</h2>
<p>{body}</p>
</>
);
}
No extra node. The parent's flex properties apply directly to both children. This matters most inside table bodies, definition lists, and any container where the browser has strict expectations about child element types.
When a div is fine
Fragments are not a rule to follow blindly. A <div> that groups things logically for styling or accessibility is a good <div>. You might need a wrapper to apply a shared hover state, or to lift state up into a parent that acts as a layout boundary. The Fragment is for the case where the wrapper serves no structural purpose and was added only to satisfy JSX.
The mental shift for me was this: ask yourself whether the wrapping element is doing a real job. If the answer is no, reach for <>...</>.
The keyed Fragment pattern
Lists are where Fragments earn their keep beyond just avoiding extra divs. When you map over data and each item produces multiple sibling elements, a Fragment with a key is both correct and clean:
{terms.map(term => (
<Fragment key={term.id}>
<dt>{term.label}</dt>
<dd>{term.definition}</dd>
</Fragment>
))}
Without the Fragment, returning two elements from map is a syntax error. Wrapping them in a <div> inside a <dl> is invalid HTML. The Fragment makes the definition list work without tricks.
If you are not sure why keys matter beyond silencing a console warning, I covered that in more detail in the article on lists and keys in React. This pattern also shows up in tables. A row that needs extra context rows uses a Fragment to group them under one key without breaking the table structure.
What changed for me
For a long time I thought Fragments were a minor convenience, something you use to avoid a console error. I was wrong. Every unnecessary wrapper div is a tiny piece of technical debt that compounds inside flex and grid layouts, inside lists, and inside strict-parent containers like <dl> and <tbody>. The Fragment is a tool for keeping the DOM exactly as flat as you intend it to be. Now I default to <> and only add a div when I can point to the specific reason it belongs there.
Newsletter
A weekly newsletter on React, Next.js, AI-assisted development, and engineering. No spam, unsubscribe any time.