Higher-Order Components (HOC)
What a Higher-Order Component Is
A higher-order component is simply a function that accepts a component as an argument and returns a new component. The returned component usually renders the original one, adding extra props, behavior, or conditional logic around it — without modifying the original component's code.
Note: Think of a HOC as a factory: withLoading(MyComponent) produces a brand new component, it doesn't change MyComponent itself.
Warning: A HOC's name (like withLoading) is just a naming convention, not a special React feature — React treats it as any other function returning a component.
Example: What a Higher-Order Component Is
<!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">
function withGreeting(WrappedComponent) {
return function Enhanced(props) {
return <div><p>Welcome!</p><WrappedComponent {...props} /></div>;
};
}
function Message({ text }) {
return <p>{text}</p>;
}
const GreetedMessage = withGreeting(Message);
function App() {
return <GreetedMessage text="This is the wrapped component" />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
A Practical HOC: withLoading
A common real-world HOC shows a loading indicator while data isn't ready yet, and renders the wrapped component once it is. This keeps the loading-check logic out of every individual component that needs it.
Note: Pass the loading condition in as a prop (like isLoading) so the same HOC can be reused with different data sources.
Warning: Always spread the remaining props ({...props}) onto the wrapped component, or props meant for it will silently disappear.
Example: A Practical HOC: withLoading
<!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">
function withLoading(WrappedComponent) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) return <p>Loading...</p>;
return <WrappedComponent {...props} />;
};
}
function UserProfile({ name }) {
return <p>Profile: {name}</p>;
}
const UserProfileWithLoading = withLoading(UserProfile);
function App() {
return <UserProfileWithLoading isLoading={false} name="Asha" />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
HOCs vs. Hooks Today
Most logic-sharing use cases that once required a HOC can now be written as a custom hook instead, without introducing an extra wrapper component in the tree. Hooks are usually preferred for new code because they don't obscure the component hierarchy the way nested HOCs can.
Note: If you're stacking more than one or two HOCs around a component, consider whether a custom hook (or several) would be clearer.
Warning: Wrapping the same component in multiple HOCs (withAuth(withLoading(withTheme(Component)))) makes debugging the component tree in DevTools noticeably harder.
Example: HOCs vs. Hooks Today
<!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">
function useLoading(isLoading) {
return isLoading;
}
function UserProfile({ name, isLoading }) {
const loading = useLoading(isLoading);
if (loading) return <p>Loading...</p>;
return <p>Profile: {name} (hook version, no wrapper component)</p>;
}
function App() {
return <UserProfile isLoading={false} name="Asha" />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Creating a new HOC-wrapped component inside another component's render, which mounts and unmounts the wrapped component (losing its state) on every render.
- Forgetting to forward extra props through to the wrapped component, silently breaking props the wrapped component expected.
- Not setting a helpful displayName on the wrapped component, making React DevTools show confusing anonymous component names.
- A higher-order component (HOC) is a function that takes a component and returns a new, enhanced component.
- HOCs are used to share cross-cutting behavior, like authentication checks or loading states, across many components.
- The naming convention is withSomething, like withAuth or withLoading.
- Custom hooks have replaced many historical HOC use cases, but HOCs still appear in some libraries (like Redux's connect()).
Works in any React version supporting function/class components (React 0.14+); a JavaScript pattern, not a React API.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: