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 incominguserprop 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 guide —
React.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?
How is a HOC different from a regular component?
Are higher order components still used in modern React?
What is the main problem with HOCs?
Should I use a HOC or a custom hook?
What does forwarding props mean in a HOC?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Program

