← Back to React
Lesson 7 · React Fundamentals

Fragments

Returning several elements without adding a wrapper div.

Beginner25 min

What you will be able to do

  • Explain what a Fragment is and why a component needs one parent
  • Write a Fragment with <>...</> and with <Fragment>
  • Decide between a Fragment and a div
  • Explain why a div around table rows breaks the table
  • Put a key on a Fragment inside map - and know why <> cannot take one
  • Recognise the warnings React gives when a Fragment is misused

The idea, in plain English

A component returns one value, so two sibling elements need something around them. A div works, but it adds a real element to the page - one more box in the layout, one more level in your CSS selectors, and sometimes an element that is not allowed where it ends up.

A Fragment is a wrapper that exists only for React. <>...</> groups its children so the component has a single return value, and then disappears: the children go straight into the parent in the DOM, as if they had been written there.

The rule of thumb is simple. If the wrapper means something - it has a class, a layout role, or semantic meaning - use a real element. If it exists only because JSX needs one parent, use a Fragment.

Worked example: A table row that breaks when you wrap it.

The problem Fragments solve

Returning <h2>Chandu</h2> and <p>Software Engineer</p> side by side fails to compile with "JSX expressions must have one parent element." Wrapping them in a div fixes the compile error but changes the page: the h2 and p are now inside a div that nobody asked for.

Most of the time that extra div is harmless. Sometimes it breaks a flex or grid layout that expected the h2 and p as direct children. And inside a table, it breaks the HTML itself.

Two ways to write a Fragment

The short syntax <>...</> is what you will write almost every time. It needs no import.

The long form is <Fragment>...</Fragment>, imported from react - or React.Fragment if you already import React. It does exactly the same thing, with one difference that matters: it can take a key.

Both compile to the same call: <>...</> becomes React.createElement(React.Fragment, null, ...children).

A Fragment leaves no trace in the DOM

Render <><h1>Hello</h1><p>React</p></> into a div, and the div contains an h1 and a p - no Fragment element, no extra wrapper. Open the Elements panel and there is nothing to find.

That is also why a Fragment cannot be styled. There is no element for a class to attach to, and React accepts only key and children on it. Pass className and React ignores it, logging "Invalid prop className supplied to React.Fragment. React.Fragment can only have key and children props."

div against Fragment
In the DOMdiv: a real element. Fragment: nothing.
className, stylediv: yes. Fragment: no - React warns and ignores them.
Event handlersdiv: yes. Fragment: no.
keydiv: yes. Fragment: only with <Fragment key>, not <>.
Use it whendiv: the wrapper has a purpose. Fragment: it exists only to satisfy JSX.

Tables: where a div really breaks

A tbody may contain only tr elements. A UserRows component that returns two rows wrapped in a div puts a div inside the tbody and the rows inside the div - invalid HTML.

React catches this in development, logging "In HTML, <div> cannot be a child of <tbody>. This will cause a hydration error." The warning is not a formality. When a page is rendered on the server, the browser parses the HTML itself, and it does not keep a div inside a table: it moves the div out and puts it before the table. The page the browser built no longer matches what React expected, and hydration fails.

Return the rows in a Fragment instead and the tbody contains exactly two tr elements - valid HTML, no warning, and it renders the same on the server and in the browser.

Watch out: The same applies to other elements with strict content rules: a ul or ol should contain li elements, a tr should contain td or th, and a dl should contain dt and dd. React does not warn about every case - a div inside a ul renders silently - so it is on you.

Keyed Fragments in lists

When map returns several elements per item - a title and a description, a heading and a rule - each group needs a wrapper, and React needs a key on each group.

The key goes on the outermost element map returns, which is the Fragment itself. The short syntax cannot take one - <key={id}> is simply not valid syntax - and a keyless <> inside map triggers "Each child in a list should have a unique key prop." Use <Fragment key={course.id}> instead.

This is the one situation where the long form is required, and the reason to know it exists.

Fragments with conditions

A condition that shows several elements needs a wrapper too: {isLoggedIn && (<><p>Welcome back!</p><button>Logout</button></>)}. With a Fragment, the paragraph and the button become direct children of whatever contains the condition.

Fragments can also be nested, but a Fragment directly inside another Fragment adds nothing - flatten it.

When to keep the div

A Fragment is not a better div. It is a different tool, for when the wrapper has no job. Keep a real element whenever the wrapper does something: carries a className for a card or a flex row, holds a click handler, or has meaning of its own.

Prefer a meaningful element over a div where one fits. A card is often an article, a group of controls a section or a form, and the page frame a header, main, and footer - which themselves are often returned side by side from a Layout component, inside a Fragment.

Syntax and examples

Two siblings: div against Fragment
// With a div function UserInfo() { return ( <div> <h2>Chandu</h2> <p>Software Engineer</p> </div> ); } // DOM: <div><h2>Chandu</h2><p>Software Engineer</p></div> // With a Fragment function UserInfo() { return ( <> <h2>Chandu</h2> <p>Software Engineer</p> </> ); } // DOM: <h2>Chandu</h2><p>Software Engineer</p>
Three spellings, one result
import React, { Fragment } from "react"; const short = ( <> <h2>Chandu</h2> <p>Software Engineer</p> </> ); const named = ( <Fragment> <h2>Chandu</h2> <p>Software Engineer</p> </Fragment> ); const namespaced = ( <React.Fragment> <h2>Chandu</h2> <p>Software Engineer</p> </React.Fragment> ); // All three compile to: // React.createElement(React.Fragment, null, h2, p)
The table that breaks
function UserRows() { return ( <div> <tr><td>Chandu</td><td>React</td></tr> <tr><td>Ravi</td><td>Node.js</td></tr> </div> ); } function UserTable() { return ( <table> <tbody> <UserRows /> </tbody> </table> ); } // Console (development): // In HTML, <div> cannot be a child of <tbody>. // This will cause a hydration error.
What the browser does with that HTML
<!-- The HTML as sent from the server --> <table><tbody><div><tr><td>Chandu</td></tr></div></tbody></table> <!-- What the browser actually builds: the div is moved out of the table --> <div></div> <table><tbody><tr><td>Chandu</td></tr></tbody></table>
The fix: return the rows in a Fragment
function UserRows() { return ( <> <tr><td>Chandu</td><td>React</td></tr> <tr><td>Ravi</td><td>Node.js</td></tr> </> ); } // DOM: // <table> // <tbody> // <tr><td>Chandu</td><td>React</td></tr> // <tr><td>Ravi</td><td>Node.js</td></tr> // </tbody> // </table>
A key on a Fragment
import { Fragment } from "react"; const courses = [ { id: 1, title: "React" }, { id: 2, title: "Node.js" }, ]; function CourseList() { return ( <> {courses.map((course) => ( <Fragment key={course.id}> <h2>{course.title}</h2> <p>Learn {course.title}</p> </Fragment> ))} </> ); } // Without the key: Each child in a list should have a unique "key" prop. // <key={course.id}> is not valid syntax - the short form takes no attributes.
Several elements behind one condition
function Dashboard({ isLoggedIn }: { isLoggedIn: boolean }) { return ( <> <h1>Dashboard</h1> {isLoggedIn && ( <> <p>Welcome back!</p> <button>Logout</button> </> )} </> ); } // isLoggedIn true: <h1>Dashboard</h1><p>Welcome back!</p><button>Logout</button>
Fragment for grouping, real elements for meaning
function Layout() { return ( <> <header>RightTech Engineering</header> <main> {/* has a class: keep the real element */} <article className="course-card"> <h2>React</h2> <p>Learn React</p> </article> </main> <footer>© 2026</footer> </> ); }
Props a Fragment rejects
<Fragment className="card"> <p>a</p> </Fragment> // Renders <p>a</p> with no class, and logs: // Invalid prop className supplied to React.Fragment. // React.Fragment can only have key and children props.

Tip: Check the Elements panel: a Fragment never appears there, only its children. If you see an unexpected wrapper div, something other than a Fragment put it there.

Fragment forms

<>...</>

The everyday form. No import, no attributes.

<>
  <h1 />
  <p />
</>
<Fragment>

The long form, imported from react. Accepts key.

import { Fragment } from "react";
<React.Fragment>

The same, through the React namespace.

<React.Fragment key={id}>
key

The only prop a Fragment takes besides children.

<Fragment key={item.id}>

Try it yourself

The code does not change. Swap the content string and the program does something else entirely.

Look for it

“Return two elements in <>...</>, open the Elements panel, and look for the Fragment. Then swap it for a div and look again.”

Break the table

“Wrap two tr elements in a div inside a tbody and read the console. Then change the div to <> and watch the warning disappear.”

Remove the key

“Map over courses returning <Fragment key={course.id}>, then remove the key. What does the console say?”

Style a Fragment

“Add className="card" to a <Fragment>. Is the class anywhere in the DOM?”

What usually goes wrong

Wrapping table rows in a div

A tbody may contain only tr elements. React warns, and server-rendered pages fail to hydrate because the browser moves the div out of the table.

✗ <div>
  <tr>...</tr>
  <tr>...</tr>
</div>
✓ <>
  <tr>...</tr>
  <tr>...</tr>
</>
A keyless short Fragment in map

Each group map returns needs a key, and <> cannot hold one.

✗ {items.map((item) => (
  <>
    <dt>{item.term}</dt>
    <dd>{item.meaning}</dd>
  </>
))}
✓ {items.map((item) => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.meaning}</dd>
  </Fragment>
))}
Putting the key on a child instead

The key belongs on the outermost element returned from map - the Fragment - not on something inside it.

✗ <>
  <h3 key={lesson.id}>{lesson.title}</h3>
  <hr />
</>
✓ <Fragment key={lesson.id}>
  <h3>{lesson.title}</h3>
  <hr />
</Fragment>
Trying to style a Fragment

There is no element to style. If the wrapper needs a class, it needs to be a real element.

✗ <Fragment className="card">
✓ <div className="card">
Replacing every div with a Fragment

A wrapper that carries layout, a class, or an event handler is doing a job. Removing it changes how the page looks and behaves.

Best practices

  • Use <>...</> when a wrapper exists only because JSX needs one parent.
  • Use a real element when the wrapper has a class, a handler, or meaning.
  • Prefer a semantic element - article, section, header - over a div when one fits.
  • Use <Fragment key={...}> whenever map returns more than one element per item.
  • Respect HTML content rules - tr in tbody, li in ul, dt and dd in dl.
  • Flatten Fragments nested directly inside other Fragments.

Practice

Write these yourself before opening anything. Getting them wrong first is most of how this sticks.

1.

Write a UserRows component that returns a row for Chandu (React) and one for Ravi (Node.js), and a UserTable that renders them inside a tbody - with no warnings.

Show hint

The rows must be direct children of the tbody in the DOM.

Show solution
function UserRows() { return ( <> <tr> <td>Chandu</td> <td>React</td> </tr> <tr> <td>Ravi</td> <td>Node.js</td> </tr> </> ); } function UserTable() { return ( <table> <tbody> <UserRows /> </tbody> </table> ); }
2.

Render a glossary from [{ id: 1, term: "JSX", meaning: "Markup in JavaScript" }, { id: 2, term: "Fragment", meaning: "A wrapper with no DOM element" }] as a dl, with one dt and one dd per entry.

Show hint

A dl may contain only dt and dd, so each pair needs a keyed Fragment.

Show solution
import { Fragment } from "react"; const glossary = [ { id: 1, term: "JSX", meaning: "Markup in JavaScript" }, { id: 2, term: "Fragment", meaning: "A wrapper with no DOM element" }, ]; function Glossary() { return ( <dl> {glossary.map((entry) => ( <Fragment key={entry.id}> <dt>{entry.term}</dt> <dd>{entry.meaning}</dd> </Fragment> ))} </dl> ); }
3.

For each wrapper, decide Fragment or element: (a) the h1 and p a component returns, (b) a course card with a border, (c) the tr elements a component returns, (d) a row of buttons laid out with flex.

Show hint

Does the wrapper need to exist on the page?

Show solution
(a) Fragment - the wrapper only satisfies the one-parent rule. (b) Element - the border needs a class on a real element (article or div). (c) Fragment - a div between tbody and tr is invalid HTML. (d) Element - flex is set on the container, so the container must exist.
Coding challenge

A lesson table with detail rows

Render each lesson as two table rows - a summary row and a detail row - with valid HTML and no console warnings.

It should
  • Use lessons: [{ id: 5, title: "JSX Fundamentals", minutes: 30, summary: "Tags become function calls." }, { id: 6, title: "JSX Expressions", minutes: 35, summary: "Braces hold expressions." }].
  • Render a table with a thead and a tbody.
  • Each lesson produces a summary row (number, title, minutes) and a detail row whose single cell spans all three columns.
  • Every tr must be a direct child of the tbody.
  • No key warnings in the console.
import { Fragment } from "react"; const lessons = [ { id: 5, title: "JSX Fundamentals", minutes: 30, summary: "Tags become function calls." }, { id: 6, title: "JSX Expressions", minutes: 35, summary: "Braces hold expressions." }, ]; function LessonTable() { // your table here }
Show one solution
import { Fragment } from "react"; const lessons = [ { id: 5, title: "JSX Fundamentals", minutes: 30, summary: "Tags become function calls." }, { id: 6, title: "JSX Expressions", minutes: 35, summary: "Braces hold expressions." }, ]; function LessonTable() { return ( <table> <thead> <tr> <th>#</th> <th>Lesson</th> <th>Minutes</th> </tr> </thead> <tbody> {lessons.map((lesson) => ( <Fragment key={lesson.id}> <tr> <td>{lesson.id}</td> <td>{lesson.title}</td> <td>{lesson.minutes}</td> </tr> <tr> <td colSpan={3}>{lesson.summary}</td> </tr> </Fragment> ))} </tbody> </table> ); } export default LessonTable;

Key points

  • A Fragment groups elements for React and adds nothing to the DOM.
  • <>...</> is the everyday form; <Fragment> is the long form, imported from react.
  • Fragments cannot be styled - React accepts only key and children on them.
  • A div around table rows is invalid HTML, warns in development, and breaks hydration.
  • When map returns several elements per item, wrap them in <Fragment key={...}>.
  • The short form cannot take a key.
  • Keep a real element when the wrapper has a class, a handler, or meaning.

Quick check before you move on

What does <><h1 /><p /></> add to the DOM besides the h1 and p?
Nothing. The Fragment exists only in React.
Why does a div around tr elements break a table?
A tbody may contain only tr elements. React warns in development, and the browser moves the div out of the table when parsing server HTML, so hydration fails.
When must you use <Fragment> instead of <>?
When the Fragment needs a key - typically when map returns more than one element per item.
Can a Fragment have a className?
No. React ignores it and warns that a Fragment can only have key and children.

Interview questions

What is a React Fragment?

A wrapper that groups children for React without adding a DOM element. It lets a component return several siblings as one value.

Why not just use a div?

A div is a real element. It can break flex or grid layouts that expect direct children, adds depth to the DOM, and is invalid in places like tbody, ul, and dl. When the wrapper has no job, a Fragment avoids all of that.

What is the difference between <> and <Fragment>?

They render the same thing. <Fragment> - or React.Fragment - can take a key; <> cannot take any attributes.

When do you need a keyed Fragment?

When map returns more than one element per item. The key must go on the outermost element returned, which is the Fragment.

Why can a div inside a tbody cause a hydration error?

The browser HTML parser does not allow a div there, so when it parses the server-rendered HTML it moves the div out of the table. The resulting DOM no longer matches what React rendered, so hydration fails.

What props does a Fragment accept?

key and children. Anything else, such as className, is ignored with a development warning.

Quiz

  1. 1.

    What does a Fragment add to the DOM? A. A div B. A span C. Nothing D. A fragment element

  2. 2.

    Which Fragment can take a key? A. <> B. <Fragment> C. Both D. Neither

  3. 3.

    A component returns two tr elements inside a div, rendered in a tbody. What happens? A. Nothing unusual B. React warns that a div cannot be a child of a tbody C. The rows disappear D. The code does not compile

  4. 4.

    Where does the key go when map returns a Fragment with an h3 and an hr? A. On the h3 B. On the hr C. On the Fragment D. On both children

  5. 5.

    A card needs a border and padding. What should wrap its contents? A. <> B. <Fragment> C. A real element such as article or div D. Nothing

Comments

Sign in to leave a comment. Your name and photo come from Google; nothing else is shared.

Loading comments...