React design patterns are reusable ways to structure components and logic so an app stays readable as it grows. The patterns that matter most with Hooks are composition, custom Hooks, compound components, controlled components, and the provider pattern.
Composition
Composition means building a component from smaller pieces instead of making one component handle every case. A card receives its content through the children prop, so the card stays reusable and the caller stays in control of what renders inside.
This is the single most important pattern, and it solves most of the problems that inheritance or giant prop lists are used for. The full mechanics are in The children Prop in React. Composition also keeps data flowing one way, which is why the React docs treat it as the default over inheritance.
Custom Hooks
A custom Hook extracts stateful logic so several components can share how it works without sharing the state itself. The logic moves into a function, and each calling component gets its own independent copy.
Custom Hooks hide the details of an external system, a browser API, or a repeated piece of state. The calling code reads like intent rather than implementation.
See How to Create a Custom Hook in React for the extraction process. The rule of thumb is to extract a Hook when the logic repeats in more than one component, not for a single use.
Compound components
A compound component is a group of components that share one hidden context, so related controls work together without the caller threading props through every level. A menu, tabs, or select is the classic case: the parts know about each other, but the caller only writes JSX.
<Tabs>
<TabList>
<Tab>Account</Tab>
<Tab>Billing</Tab>
</TabList>
<TabPanels>
<TabPanel>Account settings</TabPanel>
<TabPanel>Billing details</TabPanel>
</TabPanels>
</Tabs>Each part reads the shared tab state from context. The pattern is covered step by step in Compound Components in React.
Controlled and uncontrolled components
A component is controlled when a parent owns its value, and uncontrolled when it keeps its own state. The two differ in where the source of truth lives.
| Pattern | Data lives | Typical props |
|---|---|---|
| Controlled | Parent component | value, onChange |
| Uncontrolled | The component itself | defaultValue, ref |
A controlled input is the common form. The parent holds the value and updates it on every change.
import { useState } from "react";
function SearchBox() {
const [query, setQuery] = useState("");
return (
<input
aria-label="Search"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}The input never decides its own text. It renders the parent's value and reports changes back, which keeps the data flow in one direction. Neither form is universally better: use controlled when the parent must read or react to the value, and uncontrolled when the value only matters inside the component.
The provider pattern
The provider pattern wraps a subtree in a context provider so distant components can read shared data without prop drilling. It is the right tool for cross-cutting values such as a theme or the current user.
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext("light");
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
className={theme}
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
Toggle
</button>
);
}The provider owns the state once, and any component below reads it with useContext. Context is a tool for sharing, not a replacement for props everywhere, so check whether a plain prop would be clearer before you adopt context widely.
Patterns you will still meet
Two older patterns remain common in existing code and libraries. A higher-order component is a function that wraps a component and returns a new one, used by APIs such as Redux connect. A render prop is a prop that returns JSX.
Hooks replaced both for new code, but you will read them in older codebases and in libraries like React Router. When you meet them, recognize them as the same goals as composition and custom Hooks expressed with older syntax. Reading them is still part of the job, because most long-lived React codebases contain at least one.
Rune AI
Key Insights
- Composition is the foundation of every other React pattern.
- Custom Hooks share stateful logic, not state.
- Compound components give related controls a shared context.
- Controlled components lift state while uncontrolled ones keep it local.
- The provider pattern shares cross-cutting data without prop drilling.
Frequently Asked Questions
Are render props and higher-order components still used?
Which pattern should I learn first?
Conclusion
The patterns that matter with Hooks are composition, custom Hooks, compound components, controlled components, and the provider pattern. Learn composition first, then add each pattern only when the specific problem it solves appears.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.