πͺ Mastering State Management in React: Lifting and Colocating State
React makes it easy to build dynamic user interfaces by managing UI state. But what happens when multiple components need to share or manage that state? In this blog post, weβll walk through one of the most important concepts in React development: lifting and colocating state.
Weβll break this into three core parts:
Letβs dive in! πββοΈ
A common challenge for beginners is: βHow do I share state between sibling components?β
You move the state to the closest common parent and pass it down via props.
import { useState } from 'react'function App() {return (<div><Counter /></div>)}function Counter() {const [count, setCount] = useState(0)return (<div><button onClick={() => setCount(count + 1)}>Increment</button></div>)}
function App() {return (<div><Counter /><CountDisplay /></div>)}function CountDisplay() {return <div>Count: 0</div>}
Right now, CountDisplay doesnβt know the actual count.
To fix this, we lift the state to the App component:
function App() {const [count, setCount] = useState(0)return (<div><Counter count={count} setCount={setCount} /><CountDisplay count={count} /></div>)}function Counter({ count, setCount }) {return (<div><button onClick={() => setCount(count + 1)}>Increment</button></div>)}function CountDisplay({ count }) {return <div>Count: {count}</div>}
β Now both components are synced through shared state. This pattern of lifting state is extremely common and essential to understanding Reactβs unidirectional data flow.
Imagine we now have a blog app where each post can be βlikedβ with a heart β€οΈ. Initially, you may have implemented the like functionality inside each Card component, using local state.
function Card({ post }) {const [liked, setLiked] = useState(false)return (<div><h2>{post.title}</h2><button onClick={() => setLiked(!liked)}>{liked ? 'π' : 'π€'}</button></div>)}
But now the product team wants a new feature:
βSort posts based on whether theyβre liked.β
To achieve this, we need a global understanding of which posts are liked β that means we have to lift the liked state up to a parent, such as the MatchingPosts component.
This involves:
liked post IDs in the parent component.liked status and toggle function down as props to each card.Once lifted, we can easily sort the posts and re-render based on their liked status.
β Key takeaway: Lift state when multiple components depend on the same piece of data or when global behavior (like sorting) is needed.
After implementing and releasing the feature to sort posts by likes, users report:
βItβs confusing to have posts reorder themselves when I like something.β
So the product team decides to remove the sorting functionality. Now, no other component needs to know which posts are liked.
Does that mean the lifted state is still useful?
π Nope. Itβs time to colocate.
Since the liked state is now only needed inside the Card component, we should move it back into that component to improve:
Reverting back to this is ideal:
function Card({ post }) {const [liked, setLiked] = useState(false)return (<div><h2>{post.title}</h2><button onClick={() => setLiked(!liked)}>{liked ? 'π' : 'π€'}</button></div>)}
β Key takeaway: Colocate state whenever possible. Donβt keep it higher in the component tree than it needs to be.
π§ͺ Pro tip: Always evaluate who uses the state. If itβs just one component β keep it there.
React gives you flexibility in how you structure and manage your state. Learning when to lift and when to colocate is a hallmark of progressing from junior to senior React developer.
Hereβs a summary:
| Scenario | Action |
|---|---|
| Multiple components need shared data | Lift the state |
| Component needs isolated state | Colocate the state |
| Global behavior like sorting/filtering | Lift the state |
| State is no longer shared | Colocate it again |
π¨βπ« Practice what youβve learned: Next time youβre building a feature, ask yourself:
βWho actually needs this piece of state?β
Then decide whether to lift or colocate based on that answer.
Happy coding! π