Compound Components, Render Props, and Higher Order Components each solve real problems ā but knowing which one fits a given situation matters more than knowing all of them exist. This lesson is a practical decision framework for choosing between them, plus custom hooks, based on the actual shape of the problem.
1You Now Have a Whole Toolbox ā Which Tool Do You Reach For?
Compound Components, controlled/uncontrolled design, Render Props, and Higher Order Components each solve real, distinct problems. Effective component design isn't about favoring one pattern universally ā it's about matching the right tool to the specific shape of the problem at hand.
2Rule of Thumb #1: Default to Plain Composition
Before reaching for any advanced pattern, check whether a component accepting children or a named JSX prop already solves the problem. Most component design needs don't require Context, render props, or a HOC ā advanced patterns should be reserved for when plain composition genuinely falls short.
3Rule of Thumb #2: Multiple Cooperating Pieces? Compound Components.
When a component naturally has several visually distinct, state-sharing pieces ā like a Tabs list and its panels ā Compound Components fits well, letting the consumer arrange those pieces freely while shared state coordination happens invisibly through Context.
4Rule of Thumb #3: Data Logic ā Custom Hook. JSX Structure ā Render Prop.
For sharing pure data or behavior, a custom hook is simpler and avoids extra nesting. For sharing structural rendering logic the consumer needs to customize per use case ā like a list's per-item markup ā a render prop remains the better fit, since a hook's return value can't express JSX structure decisions.
5Rule of Thumb #4: Uniform Behavior Across Many Components? Hook First, HOC When Needed.
For cross-cutting concerns applied across many unrelated components, a custom hook called explicitly usually avoids the nested wrapper hell HOCs are prone to. HOCs still make sense in specific cases, like needing to support class components or inject props a consumer can't easily read from a hook.
6Step-by-Step Breakdown
You Now Have a Whole Toolbox ā Which Tool Do You Reach For?. You've learned Compound Components, Controlled/Uncontrolled design, Render Props, and Higher Order Components. Real component design isn't about picking a favorite pattern ā it's about matching the right tool to the specific problem in front of you. This lesson is a decision framework for exactly that.
Rule of Thumb #1: Default to Plain Composition. Before reaching for any advanced pattern, ask: can this just be solved with children or a named JSX prop? Most component design problems don't need Context, render props, or a HOC ā they need a component that accepts and renders whatever JSX it's given. Advanced patterns are for when plain composition genuinely isn't enough.
Rule of Thumb #2: Multiple Cooperating Pieces? Compound Components.. If your component naturally has several visually distinct pieces that need to share state ā a Select's trigger and options, a Tabs list and its panels ā Compound Components is the right fit, because it lets the consumer arrange those pieces freely while state coordination happens invisibly.
You're designing a Tabs widget with a tab list and separate panel content that need to stay in sync. Which pattern fits best?
- āCompound Components ā multiple cooperating pieces sharing state
- āA Higher Order Component
Rule of Thumb #3: Reusable Data Logic? Custom Hook. Reusable JSX Structure? Render Prop.. If you're sharing pure data or behavior (fetching, tracking a value), write a custom hook ā it's simpler and avoids nesting. If you're sharing structural rendering logic that the consumer must customize per use (like a list's item markup), a render prop is still the better fit, since a hook can't hand back JSX decisions.
Rule of Thumb #4: Uniform Behavior Across Many Unrelated Components? HOC or Hook.. For cross-cutting concerns like authentication checks applied to many different pages, either a HOC or a custom hook can work ā but a hook called inside each component, or combined with a layout/route wrapper, usually avoids the wrapper-hell nesting HOCs are prone to. Reach for a HOC mainly when you need to inject props a consumer can't easily read from a hook, like wrapping class components.
For most modern React codebases, what's typically the simpler default for applying an auth check across many pages?
- āA custom hook called explicitly in each component
- āAlways a Higher Order Component, regardless of context
Mastery Achieved. You now have a practical decision framework: default to plain composition with children; reach for Compound Components when several pieces need to share state; use a custom hook for pure data, a render prop for consumer-controlled markup; and prefer hooks over HOCs for most cross-cutting concerns today. Next, you'll learn the Slot Pattern for even more explicit, named composition points.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
These are component API design decisions, not browser features.
Fully applicable.
Fully applicable.
Fully applicable.
Accessibility (A11y)
1Pattern Choice Should Never Compromise Semantic Structure
Whichever pattern is chosen, verify the resulting rendered output still produces correct, accessible markup ā the composition pattern is an implementation detail, but its rendered result is what assistive technology actually interacts with.
SEO Implications
- 1
Simpler Composition Patterns Are Easier to Keep Server-Rendering-Friendly
Plain composition and compound components generally integrate more cleanly with Server/Client Component boundaries than deeply nested HOCs, since there's less indirection obscuring which parts of the tree need client interactivity.
Best Practices
Revisit Pattern Choices as Requirements Change
A component that started as a simple uncontrolled widget might later need Compound Components or controlled state as new requirements (like cross-component coordination) emerge ā don't over-engineer upfront, but be ready to refactor toward a more capable pattern when genuinely needed.
Document Why a Non-Obvious Pattern Was Chosen
If a component uses a HOC or render prop instead of the now-more-common hook approach, a short comment explaining why (e.g. 'needs to support class component consumers') saves future maintainers from wondering if it's just outdated code.
Frequent Bugs
A component was built with Compound Components and Context for a case that only ever has one consumer with no shared-state needs.
This is likely over-engineering ā a simpler component accepting children or a couple of named props would solve the same problem with far less implementation complexity. Reserve Compound Components for genuine multi-piece, state-sharing scenarios.
A team keeps writing new Higher Order Components for logic that's really just shared data-fetching.
Pure data/behavior sharing is almost always better served by a custom hook today, avoiding HOC wrapper nesting entirely. Reserve HOCs for cases with a specific reason to prefer them, like class component support.
Real-World Examples
Choosing Between Patterns for a Data Table Feature
A team building a data table needed: (1) sortable column headers sharing sort state ā solved with Compound Components; (2) reusable fetching/pagination logic ā solved with a custom hook, useTableData; and (3) fully customizable cell rendering per column ā solved with a renderCell render prop. Using the right pattern for each sub-problem kept the implementation clear instead of forcing one pattern to do everything.
function DataTable({ columns, data }) {
const { rows, sortBy, setSortBy } = useTableData(data);
return (
<Table>
<Table.Header columns={columns} sortBy={sortBy} onSort={setSortBy} />
<Table.Body rows={rows} renderCell={(row, col) => col.renderCell(row)} />
</Table>
);
}