The 4 React patterns that survive 18 months of product growth (and the 3 that collapse)
A clean React project can become a tangle in a matter of months. Here are the 4 composition patterns that hold up to growth - and the 3 that are sure to collapse, with React 19.
Why architecture determines a project's lifespan
A React project can start out flawless and turn into a tangle in a matter of months. The difference between the two trajectories is rarely down to the talent of the developers - it's down to the patterns chosen at the outset. Having worked with dozens of teams on React projects of all sizes, I've seen the same structural errors repeat themselves, and the same patterns save the day.
React 19, stable since December 2024 and in version 19.2 in 2026, has reinforced this dynamic rather than disrupting it. Server Components are stable, Actions simplify forms, the use() API reads Promises during rendering, and the React Compiler makes manual memoization less and less necessary. None of these developments outdate the fundamentals: they reinforce them. Here are the four patterns that pass the 18-month growth test - and the three that are sure to collapse.
The 4 patterns that survive
1. The server / presenter client container (RSC)
React Server Components have resurrected and modernized the old container-presenter pattern. Server components act as containers: they retrieve data, carry business logic and prepare props. Client components, marked "use client", act as presenters: they receive serializable data and manage interactivity. The serialization boundary between server and client imposes a beneficial discipline - no functions or classes crossing it - and forces a clear separation of responsibilities. Server Actions complete the model, and with useActionState and useFormStatus, a complete form can be written in a fraction of the code before.
jsx "use client; import { useActionState } from "react"; import { subscribe } from "./actions"; // 'use server'
export function SignupForm() { const [state, formAction, pending] = useActionState(subscribe, null); return ( <form action={formAction}> <input name="email" type="email" required /> <button disabled={pending}>{pending ? "..." : "Subscribe"}</button> {state?.error && <p role="alert">{state.error}</p>} </form> ); }
### 2. Compound Components
This is my favorite composition tool for complex interfaces: a set of components that share an implicit state via Context. Rather than a monolithic `Select` with twenty props, you expose `Select.Trigger`, `Select.Content`, `Select.Item`. The consumer rearranges, omits or interposes sub-components without the parent having to anticipate every case. This is composition in its purest form. React 19 also simplifies implementation, since `ref` has become a regular prop - no more `forwardRef` to pass a reference through layers, a real gain in readability in deep hierarchies.
### 3. Headless UI
The headless pattern completely dissociates interaction logic from its rendering: focus management, keyboard navigation, ARIA states on one side; no imposed style. The consumer provides the appearance, which puts an end to battles with the CSS of a third-party design system. The 2026 ecosystem is mature: Radix UI Primitives (maintained by WorkOS) serves as the foundation for shadcn/ui and has become a de facto standard; Adobe's React Aria offers exemplary accessibility coverage; Ark UI relies on state machines via Zag.js. shadcn/ui deserves a special mention: instead of installing an npm dependency, it generates the code in your project via `npx shadcn@latest add` - note that the CLI has been renamed from `shadcn-ui` to `shadcn`, and its version 4 (March 2026) now scaffolds complete templates. You own the code, you can modify it freely, there are no update constraints.
### 4. Colocation and custom hooks
For organization, I apply colocation: each feature groups its components, hooks, types and tests in a single folder. This feature-based approach makes dependencies explicit and refactorings local. And I follow the short component rule: beyond 50 to 100 lines, I extract logic in a custom hook or decompose. Custom hooks are React's most powerful reuse mechanism - a component that simply composes hooks and renders JSX can be understood, tested and maintained for years. The React Compiler reinforces this pattern by supporting memoization, making code even lighter.
## The 3 collapsing patterns
### 1. The god component and prop drilling
The component that grows to carry twenty props and three hundred lines is the first cause of debt. It becomes impossible to test, reuse or understand. Its cousin, prop drilling - taking a prop down through five levels of components that don't use it - almost always means that a Compound Component or Context should have been used. These two rarely survive six months, let alone eighteen.
### 2. Premature global status
The classic mistake: putting everything in a global blind "just in case". By 2026, the landscape has changed. Redux is in decline for new developments; Zustand has established itself with over 40 million monthly npm downloads and a minimal API; Jotai covers atomic state; and above all, TanStack Query dominates server state with its intelligent cache. The practical consequence: most of what we used to put into Redux is actually server state, which belongs to TanStack Query, or local state, which belongs to the component. The global store is the last resort, not the first reflex.
### 3. Organization by technical type
A `components` folder, a `hooks` folder, a `tools` folder: it looks neat at first, and becomes unmanageable past a certain size, because working on a feature means jumping between five folders. It's the anti-pattern that collapses most silently - nobody notices until the day a feature is added that affects thirty scattered files. Feature-based colocation solves the problem right from the start.
| Pattern | Verdict | Why |
|---|---|---|
| Container server / presenter client | survives | separation imposed by serialization |
| Compound Components | flexible composition, implicit state |
| Headless UI (Radix, shadcn) | survives | decoupled logic and appearance |
| Colocation + custom hooks | survive | explicit dependencies, local refactor |
| God component / prop drilling | collapses | untestable, unreadable |
| Global state premature | collapses | confuses server state with local state |
| Organization by technical type | collapses | feature split over 5 folders |
The common thread running through these seven cases is the same: patterns that survive impose a clear separation of responsibilities, and those that collapse dilute it. React 19 and its tools give you better tools to make the right choice, but they won't do it for you. The 18-month test isn't won with the latest framework - it's won with architectural discipline, taken from the very first commit.
From Figma to React component in 5 steps - and half the glue from before is no longer needed
The token→component pipeline that everyone copies is half out of date. Since Tailwind v4 and the Design Tokens stable spec, here are the 5 steps that really matter when moving from Figma to React.
Why do your React animations crawl at 30 fps - and the Framer Motion reflex that corrects this?
Jerky animation is almost always the result of a poorly chosen property. The reflex that corrects this, and Motion patterns (ex-Framer Motion v12) for smooth interfaces at 60 fps.
15 CSS micro-interactions without a single line of JavaScript (which required a lib yesterday)
The underline that slides, the map that flips to 3D, the animated input without display: none cobbled together: 15 micro-interactions in pure CSS, at 60 fps, without a single line of JavaScript.