Back

Understanding higher-order components in React with examples

Understanding higher-order components in React with examples

Higher-order components (HOCs) are a pattern in React that can be confusing at first, but once understood, they become a valuable tool for abstracting and reusing component logic. In this guide, you’ll learn what HOCs are, how to use them, and when they make sense — with clear examples along the way.

Key Takeaways

  • Understand what a higher-order component is in React
  • Learn how to write and use HOCs with clear code examples
  • Know when to use HOCs — and when not to

What is a higher-order component?

A higher-order component is a function that takes a component and returns a new component. It adds behavior, props, or logic to the original component — without modifying it directly.

Basic structure

const withExtraLogic = (Component) => {
  return function WrappedComponent(props) {
    // Add custom behavior here
    return <Component {...props} />;
  };
};

Think of it like a decorator: you wrap something and enhance it before passing it along.

Why use a HOC?

  • Code reuse: Abstract repeated logic
  • Cross-cutting concerns: Logging, auth, tracking
  • Prop injection: Inject props based on logic without cluttering component internals

Basic example: withLogger

const withLogger = (Component) => {
  return function WrappedComponent(props) {
    useEffect(() => {
      console.log('Mounted:', Component.name);
    }, []);
    return <Component {...props} />;
  };
};

const Hello = () => <h1>Hello</h1>;
const LoggedHello = withLogger(Hello);

// Usage
<LoggedHello />

This logs the component’s name when it mounts — without touching the original Hello component.

Another use case: withWindowWidth

This HOC adds window width as a prop to any component:

const withWindowWidth = (Component) => {
  return function WrappedComponent(props) {
    const [width, setWidth] = useState(window.innerWidth);

    useEffect(() => {
      const handleResize = () => setWidth(window.innerWidth);
      window.addEventListener('resize', handleResize);
      return () => window.removeEventListener('resize', handleResize);
    }, []);

    return <Component {...props} windowWidth={width} />;
  };
};

const DisplayWidth = ({ windowWidth }) => <p>Width: {windowWidth}px</p>;

const ResponsiveDisplay = withWindowWidth(DisplayWidth);

This lets you create a responsive component without duplicating resize logic.

HOCs vs hooks vs render props

Pattern Description Use when… HOC Function returning a new component You want to add behavior to many components Hook Reusable function using use* You want reusable stateful logic Render props Pass a function as a child You want dynamic rendering via function

In-depth comparison with hooks

Hooks are preferred for logic sharing today, but there are differences:

  • HOCs are external wrappers. Hooks run inside the component.
  • Hooks offer composability — you can call multiple hooks in one component easily.
  • Hooks work with refs and local state naturally, while HOCs need special care for ref forwarding.

Same logic, different implementation

Using a hook instead of withWindowWidth:

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}

const DisplayWidth = () => {
  const width = useWindowWidth();
  return <p>Width: {width}px</p>;
};

This version avoids the extra wrapper and is more readable in modern React.

Limitations of HOCs

  • Wrapper nesting: Too many HOCs can make the component tree hard to debug
  • Naming: Anonymous wrapper components make DevTools harder to read
  • Ref forwarding: Standard refs don’t work unless you use forwardRef

Example of ref issue

const withWrapper = (Component) => (props) => <Component {...props} />;
const FancyInput = forwardRef((props, ref) => <input ref={ref} {...props} />);

Without forwardRef, ref would point to the wrapper, not the actual input.

Best practices

  • Always pass props through: <Component {...props} />
  • Name your wrapped components for debugging:
WrappedComponent.displayName = `withLogger(${Component.displayName || Component.name})`;
  • Avoid HOCs when hooks can do the job more clearly

Conclusion

Higher-order components are a flexible way to extend and reuse component logic. While hooks are more common in modern React, HOCs still have their place — especially when you want to add logic around existing components without changing their internals. Use them when they make your code clearer, not more complex.

FAQs

Yes, though they’re less common now. Hooks have replaced many use cases, but HOCs are still useful for injecting behavior into multiple components.

A HOC wraps a component and returns a new one. A hook is a function that adds logic to a component from inside.

Only if you forward the ref using `forwardRef`. Otherwise, refs will point to the wrapper instead of the original component.

Use HOCs when you want to wrap many components with the same external logic, like auth or logging, without rewriting that logic repeatedly.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers