Not the clever ones from Twitter. The ones that show up in every real project.
There's a genre of React blog post that showcases patterns so clever they're basically puzzles. This isn't that. These are the patterns I reach for in every project because they solve real problems that come up constantly.
Controlled vs uncontrolled, and when to stop fighting it
The default React instinct is to control everything — every input, every checkbox, every select has state in the component. This is fine until you have a form with twenty fields and every keystroke triggers a re-render.
The pattern I use: uncontrolled inputs for forms that submit as a unit, controlled inputs only when you need to react to changes in real time (search, filters, live preview). React Hook Form makes the uncontrolled case clean without giving up validation.
Colocate state with where it's used
Global state management gets reached for too early. The majority of state in a typical application belongs to a single component or a small subtree. useState, lifted one level, handles most cases. Context handles the rest. A global store is for the specific cases where the data is genuinely cross-cutting — auth state, user preferences, notification queues.
When I inherit a codebase with everything in Redux, the first refactor is usually moving local state back to where it belongs.
Custom hooks are for extraction, not abstraction
The most useful custom hooks I've written are ones that extract existing logic out of components that were getting too long — not ones that create new abstractions ahead of having a need for them.
useFormState, useLocalStorage, useDebounce — these are useful because they solve a specific problem that appears in multiple places. Writing useEntityManager because it sounds smart is how you end up with an abstraction that doesn't quite fit any of the places you try to use it.
Suspense boundaries at the right level
Wrapping the entire app in one Suspense boundary means everything goes blank while anything loads. Placing a Suspense boundary around each major section means loading states are isolated and the rest of the UI stays interactive.
The right granularity is usually: one boundary per route, with additional boundaries around any section that loads data independently and can show a skeleton without breaking the surrounding layout.
Don't memo everything
The advice to wrap everything in useMemo and useCallback is common and mostly wrong. Memoization has overhead — it allocates memory, runs a comparison on every render, and adds cognitive overhead for anyone reading the code.
Memo when you've measured a problem, not before. React's re-render performance is better than most people think when the components stay small and the tree stays shallow.