Back to Blog
ReactFragmentsKeysPerformance

React Fragments and Keys Guide

Master React Fragments for cleaner JSX and keys for efficient list rendering.

B
Bootspring Team
Engineering
October 27, 2018
6 min read

Fragments and keys are essential React features for clean component structure and efficient rendering. Here's how to use them.

React Fragments#

1import { Fragment } from 'react'; 2 3// Problem: Components must return single element 4function BadComponent() { 5 // Error: Adjacent JSX elements must be wrapped 6 return ( 7 <h1>Title</h1> 8 <p>Content</p> 9 ); 10} 11 12// Solution 1: Fragment syntax 13function GoodComponent() { 14 return ( 15 <Fragment> 16 <h1>Title</h1> 17 <p>Content</p> 18 </Fragment> 19 ); 20} 21 22// Solution 2: Short syntax (preferred) 23function BetterComponent() { 24 return ( 25 <> 26 <h1>Title</h1> 27 <p>Content</p> 28 </> 29 ); 30}

Why Not Div Wrappers?#

1// Using div creates extra DOM nodes 2function TableRow({ items }) { 3 return ( 4 <div> {/* This breaks table structure */} 5 <td>{items.name}</td> 6 <td>{items.value}</td> 7 </div> 8 ); 9} 10 11// Invalid HTML result: 12// <table> 13// <tr> 14// <div> <-- Invalid! 15// <td>...</td> 16// </div> 17// </tr> 18// </table> 19 20// Fragment solves this 21function TableRow({ items }) { 22 return ( 23 <> 24 <td>{items.name}</td> 25 <td>{items.value}</td> 26 </> 27 ); 28} 29 30// Valid HTML result: 31// <table> 32// <tr> 33// <td>...</td> 34// <td>...</td> 35// </tr> 36// </table>

Fragments with Keys#

1// Short syntax doesn't support keys 2// Use Fragment when you need keys 3 4function Glossary({ items }) { 5 return ( 6 <dl> 7 {items.map(item => ( 8 // Must use Fragment, not <> 9 <Fragment key={item.id}> 10 <dt>{item.term}</dt> 11 <dd>{item.description}</dd> 12 </Fragment> 13 ))} 14 </dl> 15 ); 16} 17 18// Another example 19function Comments({ comments }) { 20 return ( 21 <> 22 {comments.map(comment => ( 23 <Fragment key={comment.id}> 24 <Comment data={comment} /> 25 <Divider /> 26 </Fragment> 27 ))} 28 </> 29 ); 30}

Understanding Keys#

1// Keys help React identify which items changed 2function TodoList({ todos }) { 3 return ( 4 <ul> 5 {todos.map(todo => ( 6 <li key={todo.id}>{todo.text}</li> 7 ))} 8 </ul> 9 ); 10} 11 12// Keys should be: 13// 1. Unique among siblings 14// 2. Stable (don't change between renders) 15// 3. Predictable (same input = same key)

Good vs Bad Keys#

1// BAD: Using index as key 2function BadList({ items }) { 3 return ( 4 <ul> 5 {items.map((item, index) => ( 6 <li key={index}>{item.name}</li> 7 ))} 8 </ul> 9 ); 10} 11// Problems: 12// - Reordering breaks state 13// - Insertions at start break state 14// - Deletions cause incorrect updates 15 16// GOOD: Using unique ID 17function GoodList({ items }) { 18 return ( 19 <ul> 20 {items.map(item => ( 21 <li key={item.id}>{item.name}</li> 22 ))} 23 </ul> 24 ); 25} 26 27// GOOD: Using composite key 28function UserPosts({ users }) { 29 return ( 30 <div> 31 {users.map(user => 32 user.posts.map(post => ( 33 <Post key={`${user.id}-${post.id}`} post={post} /> 34 )) 35 )} 36 </div> 37 ); 38}

When Index Keys Are Okay#

1// Index keys are acceptable when: 2// 1. List is static (never reordered) 3// 2. Items have no stable IDs 4// 3. List is never filtered/sorted 5 6function StaticList() { 7 const items = ['Apple', 'Banana', 'Cherry']; 8 9 return ( 10 <ul> 11 {items.map((item, index) => ( 12 <li key={index}>{item}</li> 13 ))} 14 </ul> 15 ); 16} 17 18// But add unique keys for dynamic lists 19let nextId = 0; 20 21function DynamicList() { 22 const [items, setItems] = useState([]); 23 24 const addItem = (name) => { 25 setItems([...items, { id: nextId++, name }]); 26 }; 27 28 return ( 29 <ul> 30 {items.map(item => ( 31 <li key={item.id}>{item.name}</li> 32 ))} 33 </ul> 34 ); 35}

Key Behavior Demonstration#

1// Without proper keys - state gets confused 2function InputList() { 3 const [items, setItems] = useState([ 4 { id: 1, text: 'First' }, 5 { id: 2, text: 'Second' }, 6 ]); 7 8 const addToStart = () => { 9 setItems([ 10 { id: Date.now(), text: 'New' }, 11 ...items 12 ]); 13 }; 14 15 return ( 16 <div> 17 <button onClick={addToStart}>Add to Start</button> 18 {items.map((item, index) => ( 19 // BAD: Using index - input values get mixed up 20 <input key={index} defaultValue={item.text} /> 21 ))} 22 </div> 23 ); 24} 25 26// With proper keys - state is preserved correctly 27function CorrectInputList() { 28 const [items, setItems] = useState([ 29 { id: 1, text: 'First' }, 30 { id: 2, text: 'Second' }, 31 ]); 32 33 return ( 34 <div> 35 {items.map(item => ( 36 // GOOD: Using unique ID 37 <input key={item.id} defaultValue={item.text} /> 38 ))} 39 </div> 40 ); 41}

Resetting Components with Keys#

1// Key change = component remount 2function ProfilePage({ userId }) { 3 // When userId changes, entire form resets 4 return ( 5 <ProfileForm key={userId} userId={userId} /> 6 ); 7} 8 9// Without key, form would keep old state 10function ProfileFormWithoutKey({ userId }) { 11 const [name, setName] = useState(''); 12 13 useEffect(() => { 14 // Would need to manually reset 15 fetchUser(userId).then(user => setName(user.name)); 16 }, [userId]); 17 18 return <input value={name} onChange={e => setName(e.target.value)} />; 19} 20 21// With key, form resets automatically 22function App() { 23 const [selectedUserId, setSelectedUserId] = useState(1); 24 25 return ( 26 <div> 27 <UserList onSelect={setSelectedUserId} /> 28 <EditForm key={selectedUserId} userId={selectedUserId} /> 29 </div> 30 ); 31}

Generating Keys#

1// Using crypto.randomUUID (modern browsers) 2function addItem(text) { 3 const newItem = { 4 id: crypto.randomUUID(), 5 text 6 }; 7 setItems([...items, newItem]); 8} 9 10// Using counter (simple approach) 11let idCounter = 0; 12function generateId() { 13 return ++idCounter; 14} 15 16// Using nanoid library 17import { nanoid } from 'nanoid'; 18 19function addItem(text) { 20 setItems([...items, { id: nanoid(), text }]); 21} 22 23// From database 24// Usually items from backend have IDs 25const users = await fetchUsers(); 26// users already have user.id

Fragments in Conditional Rendering#

1function ConditionalContent({ showExtra }) { 2 return ( 3 <div> 4 <h1>Main Content</h1> 5 6 {showExtra && ( 7 <> 8 <p>Extra paragraph one</p> 9 <p>Extra paragraph two</p> 10 </> 11 )} 12 13 <footer>Footer</footer> 14 </div> 15 ); 16} 17 18// Multiple conditions 19function StatusDisplay({ status }) { 20 return ( 21 <div> 22 {status === 'loading' && ( 23 <> 24 <Spinner /> 25 <p>Loading...</p> 26 </> 27 )} 28 29 {status === 'error' && ( 30 <> 31 <ErrorIcon /> 32 <p>Something went wrong</p> 33 <RetryButton /> 34 </> 35 )} 36 37 {status === 'success' && ( 38 <> 39 <SuccessIcon /> 40 <p>Done!</p> 41 </> 42 )} 43 </div> 44 ); 45}

Nested Fragments#

1function NestedStructure() { 2 return ( 3 <> 4 <Header /> 5 <> 6 <Sidebar /> 7 <Main> 8 <> 9 <Article /> 10 <Comments /> 11 </> 12 </Main> 13 </> 14 <Footer /> 15 </> 16 ); 17} 18 19// Usually better to use components 20function BetterStructure() { 21 return ( 22 <> 23 <Header /> 24 <Layout> 25 <Content /> 26 </Layout> 27 <Footer /> 28 </> 29 ); 30}

Common Patterns#

1// Return array from render 2function ReturnArray() { 3 // Can return array with keys 4 return [ 5 <li key="a">Item A</li>, 6 <li key="b">Item B</li>, 7 <li key="c">Item C</li> 8 ]; 9} 10 11// Map with fragment wrapper 12function WrapperPattern({ items }) { 13 return ( 14 <> 15 {items.map(item => ( 16 <Fragment key={item.id}> 17 <ListItem item={item} /> 18 {item.isLast || <Divider />} 19 </Fragment> 20 ))} 21 </> 22 ); 23} 24 25// Fragment in higher-order component 26function withWrapper(Component) { 27 return function Wrapped(props) { 28 return ( 29 <> 30 <Component {...props} /> 31 <Analytics /> 32 </> 33 ); 34 }; 35}

Best Practices#

Fragments: ✓ Use <> for simple grouping ✓ Use <Fragment> when keys needed ✓ Avoid unnecessary wrapper divs ✓ Keep DOM structure clean Keys: ✓ Use unique, stable IDs ✓ Use database IDs when available ✓ Generate IDs at creation time ✓ Use key to force remount Avoid: ✗ Index as key for dynamic lists ✗ Random keys on each render ✗ Non-unique keys among siblings ✗ Unnecessary Fragment nesting Performance: ✓ Keys enable efficient diffing ✓ Stable keys prevent remounting ✓ Fragments reduce DOM nodes ✓ Proper keys preserve state

Conclusion#

Fragments let you group elements without adding extra DOM nodes - use the short syntax <> when possible, and <Fragment> when you need keys. Keys help React efficiently update lists - always use unique, stable identifiers. Avoid index keys for dynamic lists, and remember that changing a key forces a component to remount, which can be useful for resetting state.

Share this article

Help spread the word about Bootspring