What is a React pattern?
A React pattern is a reusable, named solution to a recurring component-design problem. This track assumes you can already build with React and teaches the patterns that keep components simple as an app scales — composition, state, boundaries.
You finished the React track: you know hooks, the rendering model, effects, and Suspense. You can build a feature. Then the feature grows — three more states, a second consumer, a designer asks for one more variant — and the component that was clean at 60 lines becomes a 400-line tangle of props, flags, and effects that everyone is afraid to touch. The React APIs didn’t fail you. The composition did.
This track is about that next layer: not what useState does, but how to arrange components, state, and boundaries so the code stays simple as the app scales. That arrangement knowledge has a name — patterns — and senior React work is largely knowing which one a situation is asking for, and which one it isn’t.
After this lesson you can define a React pattern as a named, reusable solution to a recurring component-design problem; explain why patterns matter more as an app scales (complexity compounds); read the map of this track as a progression from composition to state to boundaries; and apply the one test that separates a useful pattern from over-engineering — does it make the likely next change cheaper?
The foundation every React pattern builds on: UI is a function of state — UI = f(state). You don’t imperatively mutate the DOM; you declare what the UI should look like for the current state, and React reconciles. Every pattern in this track is a different answer to the same two questions that fall out of that model: where does the state live? and how is the rendering composed from it?
// the whole model in one line: given state, describe the UI
function Counter({ count }: { count: number }) {
return <p>Count: {count}</p>;
}Hold onto this: “composition”, “state colocation”, “the server/client boundary” are not unrelated tricks — they are all about shaping f and locating state well.
A pattern is a named, reusable solution to a recurring problem — the name is half the value. “Lift this state up”, “make these compound components”, “extract a custom hook”, “put an error boundary above that Suspense” — each names a problem you’ll meet repeatedly and a shape that solves it. The name lets a team say what to do in three words and reason about tradeoffs without re-deriving them. A pattern is not a library or a snippet to paste; it’s a design decision with known consequences.
The recurring problems patterns address in React cluster into a few families:
- Composition — how components combine (children, slots, compound components, render props).
- State — where state lives and how it flows (colocation, lifting, context, external stores).
- Effects & performance — synchronizing with the outside world, and not re-rendering needlessly.
- Boundaries — server vs client, Suspense, error boundaries, forms and actions, data fetching.
Patterns matter more as the app scales, because complexity compounds. A 5-component app tolerates almost any structure. At 500 components, a bad choice — state stored too high so everything re-renders, logic duplicated across components, a god component that owns ten concerns — multiplies into thousands of unnecessary renders, tangled change-coupling, and features no one can add safely. Patterns are how you keep the cost of the next change flat instead of letting it bend upward as the codebase grows.
// scales badly: one component owns fetching, filtering, sorting,
// pagination, selection, and rendering — every change touches all of it
function UserTable() { /* 400 lines, 9 useStates, 4 effects */ }
// scales well: each concern is a composable piece with one job
// <DataTable> + useUsers() + <Toolbar> + <Pagination>You don’t need patterns to make small things work. You need them to keep large things changeable.
The senior test for any pattern: does it make the likely next change cheaper — without overpaying now? Every pattern has a cost: indirection, more files, a concept to learn. A compound-component API is wonderful for a Tabs widget reused across the app and absurd for a one-off two-button row. Context solves prop-drilling and, misused, re-renders half your tree on every keystroke. So the discipline isn’t “apply patterns”; it’s “match the pattern to the pressure”. Ask: what is likely to change or be reused here, and does this pattern make that cheap while staying simple for everything else?
This track teaches each pattern with its failure mode — when it pays off and when it’s over-engineering — so you reach for the right one, not the fanciest one.
Same feature, two arrangements — watch a pattern earn its keep. A “user picker” needs a search box and a list, sharing the search query.
Arrangement A bolts everything into one component and drills props:
function UserPicker() {
const [q, setQ] = useState("");
const [selected, setSelected] = useState<string | null>(null);
// search input, filtering, list rendering, selection — all here
return (
<div>
<input value={q} onChange={(e) => setQ(e.target.value)} />
<ul>{/* filter + map + selection logic inline */}</ul>
</div>
);
}Fine today. But when a second screen needs the same picker with a different list layout, A can’t be reused without copy-paste. Arrangement B applies composition + state colocation: the shared state sits in the parent, the pieces are composable.
function UserPicker({ children }: { children: React.ReactNode }) {
const [q, setQ] = useState("");
return <PickerContext.Provider value={{ q, setQ }}>{children}</PickerContext.Provider>;
}
// usage — markup varies per screen, behaviour is shared
<UserPicker>
<UserPicker.Search />
<UserPicker.List render={(u) => <CompactRow user={u} />} />
</UserPicker>B costs more up front: a context, a compound API. That cost is only worth it because reuse-with-varying-markup is the likely next change. If it weren’t, A would be the senior choice. That judgment — pattern matched to the actual pressure — is the whole game, and the rest of this track is the catalogue of patterns and the pressures each one answers.
▸Why this works
Why not just learn libraries (Redux, TanStack Query, a component kit) instead of patterns? Because libraries implement patterns, and choosing or configuring one well requires understanding the pattern underneath. TanStack Query is a server-state pattern; Zustand is an external-store pattern; Radix is compound components + headless behaviour. If you know the pattern, you can evaluate any library that claims to provide it, swap it, or hand-roll the 20-line version when a dependency isn’t worth it. Patterns outlive libraries.
▸Common mistake
The most common misread of “learn patterns” is “apply patterns everywhere”. Reaching for a compound-component API, a context, a custom hook, and a memo on a component that has one state and one consumer is not senior — it’s the same complexity problem wearing a fancier coat. Patterns are tools matched to pressure. A plain useState in a plain component is the right answer far more often than newcomers to this track expect. Every lesson here names when not to use its pattern for exactly this reason.
A teammate wraps a one-off row of two buttons (used on a single screen, no reuse planned) in a compound-component API with a context provider. By this track's definition, is that good use of a pattern?
A React pattern is a named, reusable solution to a recurring component-design problem. Every pattern in this track descends from one model — UI = f(state) — and answers one of its questions: how rendering is composed, where state lives, how effects and performance behave, or how the boundaries (server/client, Suspense, data, forms) are drawn. Patterns matter more as an app scales because complexity compounds: the right arrangement keeps the cost of the next change flat. But every pattern has a cost, so the senior test is never “apply the pattern” — it’s “does this make the likely next change cheaper without overpaying now?” This track walks the families in that order, each pattern taught with its failure mode, so you learn not just the patterns but the judgment of when to reach for them. You already know React; now learn how to arrange it.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.