Fragments

Published:

Several siblings problem. Start with a compact product summary. It has a heading and a paragraph, and both belong to one component:

function ProductSummary() {
  return (
    <h2>Desk lamp</h2>
    <p>Warm light for late-night reading.</p>
  );
}

root.render(<ProductSummary />);

Waiting to run

Not run yet.

This fails before React renders. The JSX parser found two adjacent elements, but a return expression needs one value.

if a component owns several sibling elements, what is the one value that it returns?

The component must return one React node that groups those siblings.

Changing the DOM #

An ordinary element can make the syntax valid:

function ProductSummary() {
  return (
    <div>
      <h2>Desk lamp</h2>
      <p>Warm light for late-night reading.</p>
    </div>
  );
}

root.render(<ProductSummary />);

Waiting to run

Not run yet.

This works, but it answers two different questions at once:

  1. it gives the component one returned value
  2. it creates a real <div> in the browser DOM

Sometimes that <div> is meaningful. It might be the card, section, or layout container that the component actually represents. When it is only there to satisfy JSX syntax, it changes the DOM tree for no product reason.

That matters most in structures where the parent expects particular children. A table row should contain cells, not an extra layout wrapper:

function PersonCells({ person }) {
  return (
    <div>
      <td>{person.name}</td>
      <td>{person.role}</td>
    </div>
  );
}

function PeopleTable() {
  const person = { name: 'Maya', role: 'Designer' };

  return (
    <table>
      <tbody>
        <tr>
          <PersonCells person={person} />
        </tr>
      </tbody>
    </table>
  );
}

root.render(<PeopleTable />);

Waiting to run

Not run yet.

The component needs to supply two <td> siblings, but the wrapper adds a <div> inside <tr>. That is not the intended table shape, and the browser may repair invalid table markup in ways that make the result surprising.

So the next constraint is:

the grouping value must not always create a host DOM element

No host element group #

React.Fragment groups children in the React tree without adding an element to the DOM.[1]

function ProductSummary() {
  return (
    <React.Fragment>
      <h2>Desk lamp</h2>
      <p>Warm light for late-night reading.</p>
    </React.Fragment>
  );
}

root.render(<ProductSummary />);

Waiting to run

Not run yet.

The component now returns one value: a fragment. The fragment contains two children. In the DOM, though, the heading and paragraph remain siblings. There is no wrapper element between the mount node and those elements.

The same repair works for the table cells:

function PersonCells({ person }) {
  return (
    <React.Fragment>
      <td>{person.name}</td>
      <td>{person.role}</td>
    </React.Fragment>
  );
}

function PeopleTable() {
  const person = { name: 'Maya', role: 'Designer' };

  return (
    <table>
      <tbody>
        <tr>
          <PersonCells person={person} />
        </tr>
      </tbody>
    </table>
  );
}

root.render(<PeopleTable />);

Waiting to run

Not run yet.

The returned fragment is one React value; its children become the two cells that <tr> needs.

The short syntax #

Fragments are common enough to have JSX shorthand:

<>
  <h2>Desk lamp</h2>
  <p>Warm light for late-night reading.</p>
</>

That is equivalent to this for grouping children:

<React.Fragment>
  <h2>Desk lamp</h2>
  <p>Warm light for late-night reading.</p>
</React.Fragment>

The short form is usually the clearest choice when a component simply needs to return sibling elements:

function ProductSummary() {
  return (
    <>
      <h2>Desk lamp</h2>
      <p>Warm light for late-night reading.</p>
    </>
  );
}

root.render(<ProductSummary />);

Waiting to run

Not run yet.

The empty opening and closing tags are not HTML. They are JSX syntax for a fragment.

Array difference #

A component can return an array of React nodes. That is useful when data produces a variable number of siblings:

return tasks.map((task) => <li key={task.id}>{task.label}</li>);

A fragment answers a different question. It groups a fixed set of sibling elements into one returned React value:

return (
  <>
    <h2>Today</h2>
    <p>No meetings.</p>
  </>
);

Both can place multiple children in the final tree. Use an array when transforming a collection of data; use a fragment when the component has a fixed group of siblings.

Mapped list fragments #

A fragment can also be the one sibling returned from each map() callback. That becomes useful when one data item needs to produce several adjacent elements.

Here, every person produces a definition term and definition description. Those two elements are one logical item, so the key belongs on their fragment.[2]

const people = [
  { id: 'maya', name: 'Maya', role: 'Designer' },
  { id: 'leo', name: 'Leo', role: 'Engineer' },
];

function TeamDirectory() {
  return (
    <dl>
      {people.map((person) => (
        <React.Fragment key={person.id}>
          <dt>{person.name}</dt>
          <dd>{person.role}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

root.render(<TeamDirectory />);

Waiting to run

Not run yet.

key is needed because map() creates an array of sibling fragments. The key identifies one person’s group of <dt> and <dd> nodes across renders.

The shorthand fragment cannot receive a key or any other prop. There is no JSX syntax for adding attributes to <>...</>.

Use the named form when the fragment itself needs a key:

<React.Fragment key={person.id}>
  <dt>{person.name}</dt>
  <dd>{person.role}</dd>
</React.Fragment>

Fragments per render #

A fragment does not preserve an old group of DOM nodes by itself. It is part of the description returned for this render, just like an ordinary element.

function Notice({ hasWarning }) {
  return (
    <section>
      <h2>Account</h2>
      {hasWarning && (
        <>
          <p>Your payment method needs attention.</p>
          <button>Update payment method</button>
        </>
      )}
    </section>
  );
}

root.render(<Notice hasWarning={true} />);
setTimeout(() => {
  console.log('first render:', mountNode.textContent);
  root.render(<Notice hasWarning={false} />);
  setTimeout(() => {
    console.log('second render:', mountNode.textContent);
  }, 0);
}, 20);

Waiting to run

Not run yet.

The condition chooses either a fragment containing two siblings or false, which React renders as nothing. On the second render the entire group disappears. The fragment did not add a wrapper and it did not add state; it only grouped the returned children.

Filling the hole #

The first component failed because adjacent JSX elements are not one return value. A <div> made the syntax valid, but it also changed the browser DOM tree.

Fragments fill that hole:

  • a component can return one fragment containing several sibling elements
  • a fragment groups children in the React tree without creating a host DOM element
  • <>...</> is the usual syntax for an unkeyed fragment
  • <React.Fragment key={...}> is required when a mapped group needs a key
  • fragments group the output of the current render; they do not create state or change the DOM structure

Final definition #

A React fragment is a React node that groups sibling children without adding a corresponding element to the browser DOM. It lets a component return multiple adjacent elements while preserving the DOM shape required by its parent.

Summary #

Fragments fill the hole after lists and keys:

  • JSX cannot return adjacent elements without a single grouping value
  • an ordinary wrapper element changes the DOM tree
  • a fragment provides the grouping value without that DOM wrapper
  • use shorthand fragments for ordinary fixed groups
  • use a keyed React.Fragment when one mapped data item produces several siblings

Notes

  1. The live demos in this article use the React 19.2.5 and react-dom 19.2.5 development modules. They focus on the rendered DOM shape and author-facing fragment syntax rather than React's internal reconciliation implementation.

References

  1. React: Fragment (opens in a new tab) · Back
  2. React: Rendering Lists (opens in a new tab) · Back