Back to Blog
ReactFragmentsComponentsJSX

React Fragments Guide

Master React Fragments for cleaner component rendering without extra DOM nodes.

B
Bootspring Team
Engineering
April 23, 2020
5 min read

Fragments let you group elements without adding extra DOM nodes. Here's how to use them.

Basic Fragment Syntax#

1import { Fragment } from 'react'; 2 3// Full syntax 4function Columns() { 5 return ( 6 <Fragment> 7 <td>Hello</td> 8 <td>World</td> 9 </Fragment> 10 ); 11} 12 13// Short syntax 14function ColumnsShort() { 15 return ( 16 <> 17 <td>Hello</td> 18 <td>World</td> 19 </> 20 ); 21} 22 23// Why not div? 24function Table() { 25 return ( 26 <table> 27 <tbody> 28 <tr> 29 {/* Using div here would be invalid HTML */} 30 <Columns /> 31 </tr> 32 </tbody> 33 </table> 34 ); 35}

When to Use Fragments#

1// Returning multiple elements 2function UserInfo({ user }) { 3 return ( 4 <> 5 <dt>Name</dt> 6 <dd>{user.name}</dd> 7 <dt>Email</dt> 8 <dd>{user.email}</dd> 9 </> 10 ); 11} 12 13function DefinitionList({ users }) { 14 return ( 15 <dl> 16 {users.map(user => ( 17 <UserInfo key={user.id} user={user} /> 18 ))} 19 </dl> 20 ); 21} 22 23// Conditional rendering 24function Content({ isLoggedIn }) { 25 return ( 26 <> 27 {isLoggedIn ? ( 28 <> 29 <Header /> 30 <Dashboard /> 31 <Footer /> 32 </> 33 ) : ( 34 <> 35 <Header /> 36 <LoginForm /> 37 </> 38 )} 39 </> 40 ); 41} 42 43// Avoiding wrapper div 44function Navigation() { 45 return ( 46 <nav> 47 <> 48 <Link to="/">Home</Link> 49 <Link to="/about">About</Link> 50 <Link to="/contact">Contact</Link> 51 </> 52 </nav> 53 ); 54}

Keyed Fragments#

1import { Fragment } from 'react'; 2 3// With keys (can't use short syntax) 4function GlossaryList({ items }) { 5 return ( 6 <dl> 7 {items.map(item => ( 8 <Fragment key={item.id}> 9 <dt>{item.term}</dt> 10 <dd>{item.definition}</dd> 11 </Fragment> 12 ))} 13 </dl> 14 ); 15} 16 17// Table rows with multiple cells 18function DataRows({ data }) { 19 return ( 20 <> 21 {data.map(row => ( 22 <Fragment key={row.id}> 23 <tr className="main-row"> 24 <td>{row.name}</td> 25 <td>{row.value}</td> 26 </tr> 27 <tr className="detail-row"> 28 <td colSpan={2}>{row.details}</td> 29 </tr> 30 </Fragment> 31 ))} 32 </> 33 ); 34} 35 36// Short syntax doesn't support keys 37// This won't work: 38// <> key={item.id} // Error!

Avoiding Extra Wrappers#

1// Problem: Extra wrapper div affects layout 2function BadLayout() { 3 return ( 4 <div className="flex-container"> 5 <FlexItems /> {/* Adds extra div, breaks flexbox */} 6 </div> 7 ); 8} 9 10function FlexItemsBad() { 11 return ( 12 <div> {/* This extra div breaks the flex layout */} 13 <div className="flex-item">Item 1</div> 14 <div className="flex-item">Item 2</div> 15 </div> 16 ); 17} 18 19// Solution: Use Fragment 20function GoodLayout() { 21 return ( 22 <div className="flex-container"> 23 <FlexItems /> {/* No extra wrapper */} 24 </div> 25 ); 26} 27 28function FlexItems() { 29 return ( 30 <> 31 <div className="flex-item">Item 1</div> 32 <div className="flex-item">Item 2</div> 33 </> 34 ); 35} 36 37// Grid layout 38function GridItems() { 39 return ( 40 <> 41 <div className="grid-item">1</div> 42 <div className="grid-item">2</div> 43 <div className="grid-item">3</div> 44 </> 45 ); 46}

With Arrays#

1// Mapping to fragments 2function TagList({ tags }) { 3 return ( 4 <div> 5 {tags.map((tag, index) => ( 6 <Fragment key={tag}> 7 {index > 0 && ', '} 8 <span className="tag">{tag}</span> 9 </Fragment> 10 ))} 11 </div> 12 ); 13} 14 15// Result: Tag1, Tag2, Tag3 16 17// Alternative: join with elements 18function TagListAlt({ tags }) { 19 return ( 20 <div> 21 {tags 22 .map(tag => <span className="tag">{tag}</span>) 23 .reduce((prev, curr, i) => ( 24 <Fragment key={i}> 25 {prev} 26 {i > 0 && ', '} 27 {curr} 28 </Fragment> 29 ))} 30 </div> 31 ); 32}

Conditional Fragments#

1// Conditional content groups 2function UserProfile({ user, isOwner }) { 3 return ( 4 <div className="profile"> 5 <h1>{user.name}</h1> 6 7 {isOwner && ( 8 <> 9 <button>Edit Profile</button> 10 <button>Settings</button> 11 </> 12 )} 13 14 {!isOwner && ( 15 <> 16 <button>Follow</button> 17 <button>Message</button> 18 </> 19 )} 20 </div> 21 ); 22} 23 24// With ternary 25function StatusBadge({ status }) { 26 return ( 27 <div className="status"> 28 {status === 'active' ? ( 29 <> 30 <span className="dot green" /> 31 <span>Active</span> 32 </> 33 ) : ( 34 <> 35 <span className="dot gray" /> 36 <span>Inactive</span> 37 </> 38 )} 39 </div> 40 ); 41}

Component Composition#

1// Higher-order component returning fragment 2function withLogging(WrappedComponent) { 3 return function LoggedComponent(props) { 4 useEffect(() => { 5 console.log('Component rendered'); 6 }); 7 8 return ( 9 <> 10 <WrappedComponent {...props} /> 11 {process.env.NODE_ENV === 'development' && ( 12 <div className="debug-info">Debug Mode</div> 13 )} 14 </> 15 ); 16 }; 17} 18 19// Render prop with fragment 20function Toggle({ children }) { 21 const [isOn, setIsOn] = useState(false); 22 23 return children({ 24 isOn, 25 toggle: () => setIsOn(!isOn), 26 }); 27} 28 29function App() { 30 return ( 31 <Toggle> 32 {({ isOn, toggle }) => ( 33 <> 34 <button onClick={toggle}>Toggle</button> 35 {isOn && <div>Content is visible!</div>} 36 </> 37 )} 38 </Toggle> 39 ); 40}

Form Elements#

1// Form field groups 2function FormFields() { 3 return ( 4 <> 5 <label htmlFor="email">Email</label> 6 <input id="email" type="email" /> 7 8 <label htmlFor="password">Password</label> 9 <input id="password" type="password" /> 10 </> 11 ); 12} 13 14function LoginForm() { 15 return ( 16 <form> 17 <FormFields /> 18 <button type="submit">Login</button> 19 </form> 20 ); 21} 22 23// Fieldset contents 24function AddressFields() { 25 return ( 26 <> 27 <div className="field"> 28 <label>Street</label> 29 <input name="street" /> 30 </div> 31 <div className="field"> 32 <label>City</label> 33 <input name="city" /> 34 </div> 35 <div className="field"> 36 <label>ZIP</label> 37 <input name="zip" /> 38 </div> 39 </> 40 ); 41}

Performance Considerations#

1// Fragments don't create DOM nodes 2// No performance overhead from wrapper elements 3 4// Before (extra div) 5<div> {/* Creates DOM node */} 6 <Child1 /> 7 <Child2 /> 8</div> 9 10// After (Fragment) 11<> {/* No DOM node */} 12 <Child1 /> 13 <Child2 /> 14</> 15 16// DOM output is cleaner 17// <div id="root"> 18// <Child1 /> 19// <Child2 /> 20// </div> 21 22// vs 23 24// <div id="root"> 25// <div> <!-- Extra wrapper --> 26// <Child1 /> 27// <Child2 /> 28// </div> 29// </div>

Common Mistakes#

1// Wrong: Key on short syntax 2function List({ items }) { 3 return items.map(item => ( 4 // <> key={item.id}> // Syntax error! 5 <Fragment key={item.id}> 6 <span>{item.name}</span> 7 </Fragment> 8 )); 9} 10 11// Wrong: Attributes on short syntax 12function Wrong() { 13 return ( 14 // <> className="wrapper"> // Can't add attributes! 15 <></> 16 ); 17} 18 19// Wrong: Using div when Fragment would work 20function Unnecessary() { 21 return ( 22 <div> {/* Why add this if you don't need it? */} 23 <Child1 /> 24 <Child2 /> 25 </div> 26 ); 27}

Best Practices#

When to Use: ✓ Returning multiple elements ✓ Avoiding layout-breaking wrappers ✓ Table rows with multiple cells ✓ Definition lists Syntax Choice: ✓ Use <></> for simple cases ✓ Use <Fragment> when you need keys ✓ Import Fragment explicitly when needed ✓ Be consistent in your codebase Avoid: ✗ Keys on short syntax (won't work) ✗ Attributes on short syntax ✗ Unnecessary wrapper divs ✗ Over-nesting fragments

Conclusion#

Fragments provide a clean way to group elements without adding extra DOM nodes. Use the short syntax for simple grouping and the full Fragment syntax when you need keys. They're especially useful for table rows, definition lists, and avoiding layout-breaking wrapper elements.

Share this article

Help spread the word about Bootspring