Higher-order components (HOCs) in React: what are they and when should you use them?

LowIntermediateReact
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

Higher-Order Components (HOCs) are a React pattern for reusing cross-cutting component logic by wrapping a component. An HOC is a function that takes a component and returns a new component with added behavior (e.g., injecting props, guarding access, subscriptions). They’re common in older/legacy React and some libraries (e.g., Redux connect), even though hooks are the modern default for new code. HOCs enable reuse but can complicate debugging and typing. Test prop collisions and display names.

Answer

Core idea

A higher-order component (HOC) is a function with the shape (Component) => Component. It wraps a “base” component and returns a new component that adds behavior around it (inject props, gate rendering, subscribe/unsubscribe, etc.).

Key rule: an HOC should be pure with respect to its input component (don’t mutate the wrapped component; compose by wrapping).

Concept

What it means

Why it matters

Input

A component ("WrappedComponent")

The HOC enhances behavior around the same UI contract

Output

A new component ("Enhanced")

You get reuse without copy-pasting logic

How it works

Wrapper renders <WrappedComponent {...props} />

Pass-through preserves flexibility and testability

Composition

withA(withB(Component))

Order matters; wrappers layer behavior

What a HOC is (and how to reason about it)
JSX
// Minimal HOC: adds logging without changing WrappedComponent
function withLogger(WrappedComponent) {
  function WithLogger(props) {
    console.log('Rendering', WrappedComponent.displayName || WrappedComponent.name || 'Component');
    return <WrappedComponent {...props} />;
  }

  WithLogger.displayName = `withLogger(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
  return WithLogger;
}

function Hello({ name }) {
  return <h1>Hello, {name}!</h1>;
}

export const HelloWithLogger = withLogger(Hello);
                  

Common use cases (where HOCs still show up)

Use case

What the HOC does

Typical examples

Inject props

Adds derived/global props to the wrapped component

connect() (Redux), feature flags, theme injection (older patterns)

Access control

Blocks/redirects/returns fallback UI when unauthorized

withAuth / withRoleGuard

Side-effect wiring

Subscribes and cleans up (events, websockets) around the wrapped component

withSubscription / withWindowListener

Error boundaries

Wraps with an error boundary component

withErrorBoundary (common in codebases without global boundaries)

Real-world places HOCs are used

Hard parts / interview-level gotchas

Gotcha

What can go wrong

Fix / best practice

Ref forwarding

Refs don’t automatically pass through wrappers

Use React.forwardRef and pass ref down

Static methods lost

Statics on the wrapped component (e.g., someStatic) aren’t present on the wrapper

Hoist statics (e.g., hoist-non-react-statics) when needed

Prop clobbering

HOC injects a prop name that collides with caller props

Namespace injected props or document the contract clearly

Wrapper noise

DevTools shows extra wrapper layers (“wrapper hell”)

Set displayName; prefer hooks for new code

Order sensitivity

withA(withB(X)) vs withB(withA(X)) can change behavior

Be intentional; document composition order

Creating HOCs in render

New component type each render → remounts/state loss/perf issues

Create HOCs once (module scope) or memoize carefully

The parts that make HOCs “hard” in practice
JSX
import React from 'react';

// HOC that forwards refs + sets displayName
function withFocusRing(WrappedComponent) {
  const WithFocusRing = React.forwardRef(function WithFocusRing(props, ref) {
    return (
      <div data-focus-ring>
        <WrappedComponent ref={ref} {...props} />
      </div>
    );
  });

  WithFocusRing.displayName = `withFocusRing(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
  return WithFocusRing;
}
                  

Modern guidance

In modern React, custom hooks are usually the first choice for reusing stateful logic because they avoid wrapper nesting and compose more cleanly. HOCs still matter for legacy code, class components, and library APIs built around wrapping (e.g., Redux connect in older setups).

Summary

An HOC is a function: (WrappedComponent) => EnhancedComponent.

Prefer custom hooks for new logic reuse; HOCs mostly show up in legacy code and library APIs (e.g., older Redux connect).

Key gotchas: prop collisions, ref forwarding (forwardRef), hoisting statics, and wrapper nesting ("wrapper hell").

Summary

Practical scenario
Wrap components with an auth HOC that injects user info and guards rendering.

Common pitfalls

      • Prop name collisions between HOC and wrapped component.
      • Losing static methods or display names.
      • Over-nesting HOCs and reducing readability.
Trade-off or test tip
HOCs are powerful but add indirection. Test prop passing and component names in DevTools.

Similar questions
Guides
26 / 41