UserDashboard knows about APIs and state. UserList and UserCard know nothing about where data comes from — they just display what they receive. This separation makes presentational components reusable across your app.
You don’t need to force every component into one category. This is a guideline, not a rule. The goal is simple: push state management up and keep display components focused on rendering.
When two sibling components need the same data, move the state to their shared parent:
Copy
// ❌ Problem: SearchBar and UserList both need the search term,// but they're siblings — they can't share state directly// ✅ Solution: Lift state up to the parentfunction UserPage() { const [users, setUsers] = useState([]); const [search, setSearch] = useState(""); // State lives here useEffect(() => { fetch("/api/users") .then(r => r.json()) .then(data => setUsers(data)); }, []); const filtered = users.filter(user => user.name.toLowerCase().includes(search.toLowerCase()) ); return ( <div> <SearchBar search={search} onSearchChange={setSearch} /> <UserList users={filtered} /> </div> );}function SearchBar({ search, onSearchChange }) { return ( <input type="text" value={search} onChange={e => onSearchChange(e.target.value)} placeholder="Search users..." /> );}function UserList({ users }) { return ( <ul> {users.map(user => ( <li key={user.id}>{user.name} — {user.email}</li> ))} </ul> );}
UserPage owns the state. SearchBar updates it via a callback. UserList reads the filtered result. Neither child knows about the other — the parent coordinates everything.
“Lifting state up” is the answer to “how do sibling components share data?” Move the state to the nearest common parent. The parent passes data down via props and receives updates via callback props.
The data flow is clear: TodoApp owns all the state and logic. Children communicate back by calling the callback props (onAdd, onToggle, onDelete). No child modifies state directly.
Notice the pattern: DashboardPage handles all API calls and state. CreateUserForm has local state for the input fields, but the actual “create” logic lives in the parent. UserTable is pure display.
State goes in the highest component that needs it — not higher, not lower
If two components need the same data — lift the state to their parent
Smart components fetch and manage — presentational components display
Callback props for child → parent — never modify parent state directly from a child
Local state is fine for UI-only state — form inputs, open/closed toggles, hover states
Don’t try to make every component “dumb.” Form components naturally need local state for their inputs. The goal isn’t zero state in children — it’s putting shared state in the right place and keeping API logic in smart components.
You’ve learned the React essentials — components, props, state, effects, conditional rendering, lists, forms, and composition. These 8 concepts cover 90% of what you’ll do in React.Now let’s put it all together. In the next section, you’ll connect your React frontend to a FastAPI backend and build a complete full-stack application.