ReactPatternsbeginner
Updated:

Higher Order Components

6 min read

A clear guide to higher order components in React: how HOCs wrap components to share logic, real examples, common pitfalls, and when hooks replace them.

TL;DR – Quick Answer

A higher order component (HOC) is a function that takes a component and returns a new component with extra behaviour or props added. It is React's older pattern for sharing logic — like authentication checks or data loading — across many components without repeating code. Today custom hooks cover most of the same needs, but HOCs still appear in libraries and legacy code, so you must be able to read and write them.

On This Page

Imagine five different screens that all need to check "is the user logged in?" before they render. You could copy that check into all five components — or you could write it once and wrap each screen with it. That wrapper is a higher order component, and it is one of React's oldest answers to the question of how to reuse logic without repeating it.

A higher order component (HOC) is simply a function that takes a component and returns a new component with something added. You will meet HOCs in libraries, in interview questions, and in older codebases, so even in the age of hooks you need to read and write them fluently.

The idea: a function that returns a component

The name comes from higher order functions — functions that take or return other functions. A HOC does the same thing one level up: it takes a component and returns a component.

// A HOC is a function: Component in, enhanced Component out
function withGreeting(WrappedComponent) {
  return function Enhanced(props) {
    return (
      <div>
        <p>Welcome back!</p>
        <WrappedComponent {...props} />
      </div>
    );
  };
}

// Usage: produce a new component, then render it
const DashboardWithGreeting = withGreeting(Dashboard);

Read that carefully. withGreeting does not render anything by itself — it returns a new component, Enhanced, which renders the greeting and then the original component. By convention HOC names start with with, signalling "this adds something to whatever you pass in".

The critical line is <WrappedComponent {...props} />. The HOC forwards every prop it received down to the wrapped component. Drop that spread and you silently swallow the caller's props — the single most common HOC bug.

A real example: guarding a route with withAuth

Greetings are cute; access control is the reason HOCs earned their keep. Here is a HOC that only renders its wrapped component when a user is authenticated, and otherwise shows a login prompt:

function withAuth(WrappedComponent) {
  return function AuthGuard(props) {
    const { user } = useContext(AuthContext);

    if (!user) {
      return <p>Please log in to view this page.</p>;
    }
    // Authenticated: render the real component, and pass user down
    return <WrappedComponent {...props} user={user} />;
  };
}

const ProtectedDashboard = withAuth(Dashboard);
const ProtectedSettings = withAuth(Settings);

One function now protects any number of screens. Dashboard and Settings stay focused on their own job and never repeat the auth check. The HOC also adds a user prop, which is the second thing HOCs do well: inject data the wrapped component needs without the parent wiring it up every time.

Pro tip: Always forward props with {...props} first, then add your own props after. Order matters — if you write <Wrapped user={user} {...props} />, an incoming user prop from the caller would overwrite yours. Spread first, override second (or the other way, deliberately) — but always decide on purpose.

Injecting props: the classic data HOC

Before hooks, HOCs were the main way to attach shared data to a component. A withUsers HOC might load a list once and hand it to whatever it wraps:

function withUsers(WrappedComponent) {
  return function WithUsers(props) {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      fetch("/api/users")
        .then((res) => res.json())
        .then((data) => {
          setUsers(data);
          setLoading(false);
        });
    }, []);

    return (
      <WrappedComponent {...props} users={users} loading={loading} />
    );
  };
}

// UserList just receives users and loading as props — it fetches nothing
const UserListWithData = withUsers(UserList);

UserList becomes a pure presentational component: it takes users and loading and renders them, with no idea where the data came from. That separation — logic in the HOC, presentation in the wrapped component — is the pattern's real value. The useState and useEffect used here are the same hooks explained in the hooks introduction and the useState and useEffect deep dive; a HOC just packages them for reuse.

Rules every correct HOC follows

HOCs are easy to get subtly wrong. Four rules keep yours safe:

  • Forward all props. Pass {...props} to the wrapped component so you never drop the caller's data.
  • Don't mutate the input component. Return a new component. Never edit WrappedComponent.prototype — that breaks the original everywhere it is used.
  • Pick unique prop names. If two HOCs both inject a prop called data, the outer one wins and the inner value vanishes. Name injected props specifically.
  • Copy static methods and set a display name for debugging. Set Enhanced.displayName = \withAuth(${WrappedComponent.name})`` so React DevTools shows a useful name instead of "Anonymous".

Common mistake: Creating the HOC inside another component's render. Writing const Wrapped = withAuth(Dashboard) in the body of a render means a brand-new component type is created on every render, so React unmounts and remounts the subtree each time — losing all its state. Create wrapped components once, at module level.

Composing HOCs

Because a HOC takes a component and returns a component, HOCs compose naturally — you can wrap a component in several of them. The nesting reads inside-out, like function calls:

// theme is the innermost wrapper, auth the outermost
const Enhanced = withAuth(withUsers(withTheme(Dashboard)));

Dashboard is first wrapped with theme, then that result is wrapped with users, then that result is wrapped with auth. At render time the outer HOC runs first, so withAuth decides whether to render at all before withTheme ever gets a chance. Some codebases tidy this up with a small compose helper so the order reads left to right, but the mechanics are the same. The important thing is to keep the number of stacked HOCs small — each layer adds a wrapper to the tree and a little indirection to trace, which is exactly the nesting problem the next section warns about.

HOCs vs custom hooks

Since hooks arrived, most logic that used to live in a HOC lives in a custom hook instead. Compare the two approaches to the auth example:

// Same job as withAuth, as a custom hook
function useAuth() {
  const { user } = useContext(AuthContext);
  return user;
}

function Dashboard() {
  const user = useAuth();
  if (!user) return <p>Please log in.</p>;
  return <h1>Hello, {user.name}</h1>;
}

The hook version has no wrapper component, no prop forwarding, and no nesting. Data flow is obvious: Dashboard calls useAuth and you can see exactly where user comes from. This is why the community moved toward hooks for sharing stateful logic between function components.

So when do you still reach for a HOC? Three cases: when you must wrap a component you do not own or control; when you want to add a wrapper around any component type generically (including class components); and when a library's API expects the HOC shape, as several still do. Knowing both patterns — and when each fits — is exactly the kind of judgement the React freshers interview questions probe.

Watch out for wrapper hell and performance

Stack three or four HOCs — withAuth(withUsers(withTheme(Dashboard))) — and your DevTools tree fills with wrapper layers, making it hard to trace where a prop originated or why a component re-rendered. This "wrapper hell" is the practical reason HOCs fell out of favour.

There is a performance angle too. Because a HOC returns a new component, careless use can trigger extra renders. If a wrapped component is expensive, the same tools from the performance optimization guideReact.memo and stable props — apply to the wrapper just as they do to any component.

What to take away

A higher order component is nothing exotic: a function that takes a component and returns an enhanced one, forwarding props and adding behaviour. You now know the three jobs HOCs do — wrapping UI, guarding rendering, and injecting data — and the rules that keep them correct.

For new code, prefer a custom hook when you are sharing stateful logic. Keep the HOC pattern in your toolkit for the libraries and legacy screens that use it, and for interviews where explaining the difference clearly sets you apart. At CodeBegun's Java Full Stack with AI program in Hyderabad, students refactor a HOC into a hook and back again, so the trade-off between the two patterns becomes something you can reason about, not just recite.

Frequently Asked Questions

What is a higher order component in React?
A higher order component is a function that accepts a component as input and returns a new, enhanced component. It wraps the original to inject props, add behaviour, or guard rendering. The name comes from higher order functions, which take or return other functions.
How is a HOC different from a regular component?
A regular component takes props and returns UI. A HOC takes a component and returns another component, so it operates one level up. You call the HOC to produce a wrapped component, then render that wrapped component like any other.
Are higher order components still used in modern React?
Less than before. Custom hooks now handle most logic sharing more cleanly, without the wrapper nesting HOCs create. But HOCs still appear in libraries like React Router and Redux legacy APIs and in existing codebases, so you need to understand them to work professionally.
What is the main problem with HOCs?
Wrapper hell and prop collisions. Stacking several HOCs creates deep nesting that is hard to debug, and two HOCs can accidentally pass the same prop name and overwrite each other. HOCs also make it harder to see where a prop actually came from.
Should I use a HOC or a custom hook?
Prefer a custom hook when you are sharing stateful logic between function components, because hooks avoid extra wrappers and keep data flow visible. Use a HOC when you must wrap a component you do not control, add a wrapper around any component type, or match a library that expects the HOC shape.
What does forwarding props mean in a HOC?
Forwarding props means the HOC passes through all the props it received to the wrapped component using the spread operator, so it does not silently drop them. A HOC that adds one prop should still pass along every other prop the caller gave it. Forgetting to forward props is the most common HOC bug.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 15 July 2026 LinkedIn
Chat with us