Common React Design Patterns
In this page:
Why Learn Named Patterns
Giving common code shapes names (like 'compound components' or 'render props') lets developers communicate design ideas quickly, and recognize familiar structures in unfamiliar codebases faster, rather than re-deriving how something works from scratch every time.
Note: This course covers each major pattern in its own dedicated tutorial — this one focuses on when and why to reach for which pattern.
Warning: Knowing a pattern's name isn't the goal by itself — the goal is recognizing which real problem it actually solves.
Example: Why Learn Named Patterns
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// Recognizing the shape:
function useToggle(initial) {
const [value, setValue] = React.useState(initial);
return [value, () => setValue(v => !v)];
}
// "Oh, this is the custom hook pattern for shared stateful logic"
function App() {
const [isOn] = useToggle(false);
return <p>{isOn ? "On" : "Off"}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Modern React Favors Hooks Over Older Patterns
Patterns like render props and higher-order components were the primary way to share logic between components before Hooks existed. Today, a custom hook usually accomplishes the same goal with less nesting and clearer code, so hooks are generally the first tool to reach for in new code.
Note: When you see a render-props or HOC pattern in an older codebase, it's often a good candidate to refactor into an equivalent custom hook.
Warning: Not every render-props/HOC usage needs to be immediately rewritten — refactor opportunistically, not as a disruptive, all-at-once rewrite.
Example: Modern React Favors Hooks Over Older Patterns
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// Older pattern (HOC):
function withCounter(Wrapped) {
return function(props) {
const [count, setCount] = React.useState(0);
return <Wrapped {...props} count={count} increment={() => setCount(count + 1)} />;
};
}
// Modern equivalent (custom hook) is usually preferred for new code:
function useCounter() {
const [count, setCount] = React.useState(0);
return { count, increment: () => setCount(c => c + 1) };
}
ReactDOM.createRoot(document.getElementById('root')).render(<p>See dedicated pattern tutorials for full examples</p>);
</script>
</body>
</html>
Choosing the Simplest Pattern That Fits
Every pattern trades some simplicity for some flexibility or reusability. Applying a complex pattern (like compound components) to a problem that only ever needs one straightforward implementation adds unnecessary abstraction — save it for when the flexibility is actually needed.
Note: A good question before reaching for an advanced pattern: 'will this actually be reused or varied, or am I just anticipating a need that may never come'?
Warning: Premature abstraction (building a flexible, pattern-based solution for a problem that doesn't need it yet) often makes code harder to understand than a simple, direct implementation would have been.
Example: Choosing the Simplest Pattern That Fits
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// Simple, direct — fine if this is genuinely a one-off:
function SubmitButton() {
return <button type="submit">Submit</button>;
}
// Only reach for a more elaborate pattern (compound components, render props)
// once you actually need multiple, meaningfully different variations.
ReactDOM.createRoot(document.getElementById('root')).render(<SubmitButton />);
</script>
</body>
</html>
- Applying a pattern (like render props or HOCs) reflexively out of habit, when a simpler custom hook would do the same job more clearly.
- Not recognizing a familiar pattern in someone else's code, and rewriting it unnecessarily from scratch.
- Over-abstracting too early, building a flexible pattern for a case that only ever needs one simple, concrete implementation.
- Common React patterns include compound components, render props, HOCs, custom hooks, and controlled/uncontrolled components.
- Recognizing these patterns in existing code speeds up understanding unfamiliar codebases.
- Modern React often favors custom hooks over older patterns like render props or HOCs for sharing logic.
- Patterns are tools, not rules — use the simplest one that solves the actual problem at hand.
No React-version restriction — these are conventions and idioms, not specific APIs (except where noted, e.g. Context stable since React 16.3).
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Error Boundaries
- React Portals
- Modals using Portals (practical use)
- React Suspense
- Code Splitting with React.lazy
- Introduction to Server Components
- Introduction to Next.js (server-side React)
- Using React with TypeScript
- Scalable Folder Architecture
- Common React Design Patterns
- Component Documentation with Storybook
- Accessibility (a11y) in React
- i18n with react-i18next
- React Security Best Practices