← Back to React Course | Chapter 6: Custom Hooks & Advanced Patterns | Lesson 8 of 10

Higher-Order Components (HOC)

A higher-order component is like a gift wrapper — it takes any component you give it and wraps it with some extra behavior before handing it back.

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

markup
<!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

markup
<!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

markup
<!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>
Common Mistakes
  1. Creating a new HOC-wrapped component inside another component's render, which mounts and unmounts the wrapped component (losing its state) on every render.
  2. Forgetting to forward extra props through to the wrapped component, silently breaking props the wrapped component expected.
  3. Not setting a helpful displayName on the wrapped component, making React DevTools show confusing anonymous component names.
Chapter Summary
  • 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()).
Browser Support

Works in any React version supporting function/class components (React 0.14+); a JavaScript pattern, not a React API.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.