Why HOCs Exist
React’s component model handles props and state well, but sharing logic between components is a different problem. If two components need the same data-fetching, authentication-checking, or styling behavior, duplicating that code is wasteful and error-prone. Higher-order components exist to solve exactly this kind of reuse problem.
A higher-order component is a function that takes a component and returns a new component with extra capabilities. The concept comes from higher-order functions in JavaScript, where a function can accept another function as an argument and return a new function. React components are functions (or classes), so the same pattern applies naturally to them.
Higher-Order Functions, Briefly
Before looking at HOCs, it helps to recall how higher-order functions work in plain JavaScript. These are functions that operate on other functions — either taking them as arguments or returning them. They allow you to abstract over actions rather than just values.
JavaScript already ships with several: .forEach() runs the same code against every array element without mutating the array; .map() transforms each element and builds a new array from the results; .reduce() executes a function against each array value left to right; .filter() returns a new array containing only the elements that satisfy a condition.
Custom higher-order functions follow the same idea. Suppose you need to format numbers as currency with customizable currency symbols and decimal separators. You could write one function that takes those options and returns a formatter:
const formatCurrency = function(
currencySymbol,
decimalSeparator ) {
return function( value ) {
const wholePart = Math.trunc( value / 100 );
let fractionalPart = value % 100;
if ( fractionalPart < 10 ) {
fractionalPart = '0' + fractionalPart;
}
return `${currencySymbol}${wholePart}${decimalSeparator}${fractionalPart}`;
}
}
The returned function receives the value to format, extracts its whole and fractional parts, and assembles the result using the captured currency symbol and decimal separator. To use it, you create a specialized formatter and call it:
> getLabel = formatCurrency( '$', '.' );
> getLabel( 1999 )
"$19.99" //formatted value
> getLabel( 2499 )
"$24.99" //formatted value
Here getLabel is the specialized function produced by formatCurrency. Each call to the higher-order function creates a new formatter with the settings you passed in.
What a HOC Actually Does
A higher-order component applies this same compositional pattern to React components. It takes a component as input and returns a new, enhanced component. This new component can render the original one, optionally wrapping it with additional props, state, or UI.
Three facts about HOCs matter most:
- They never modify or mutate the input component — they create a new one.
- They are used to compose components for code reuse.
- They are pure functions: no side effects, only the returned component.
Examples of HOCs you may already have seen in real code include connect() from Redux and withRouter() from React Router. Both take a component and return a new version augmented with state-management or routing props.
Anatomy of a HOC
The structural rules of a HOC mirror those of higher-order functions:
- It is itself a component.
- It accepts another component as an argument.
- It returns a new component.
- The returned component can render the original passed-in component.
In code, the pattern looks like this:
import React from 'react';
// Take in a component as argument WrappedComponent
const higherOrderComponent = (WrappedComponent) => {
// And return another component
class HOC extends React.Component {
render() {
return <WrappedComponent />;
}
}
return HOC;
};
The outer function higherOrderComponent receives a WrappedComponent and returns a new component that has access to it. Any shared logic you need to attach lives inside the returned component, and it renders the wrapped one with whatever props you choose to pass along.
Putting a HOC to Work
To make this concrete, consider building a HOC from an existing component. You create the HOC as a function that wraps your component, applying the shared behavior — say, fetching data or checking a user’s permissions — in the wrapper and passing the result down as props. Every component you wrap with this HOC gains that behavior automatically, without duplicating the underlying implementation.
This is where the don’t-repeat-yourself principle comes in. Rather than writing the same subscription logic in five components, you write it once in a HOC and apply it wherever needed. The result is code that is easier to read, easier to debug, and less prone to bugs caused by inconsistent copies of the same logic.
Because a HOC returns a brand-new component with the original left untouched, you can also use it to add behavior conditionally. If a user lacks the right permissions, the HOC may return a different component entirely — a login prompt, for instance — instead of the protected view. The key constraint is that all of this decision-making happens inside the HOC’s returned component, leaving the original component purely presentational.
Higher-order components fit best when you need to share logic across many components that otherwise have unrelated implementations. For state-to-props style sharing, the normal context or props patterns remain appropriate; HOCs are an additional tool, not a replacement. Where they shine is in packaging up a specific, repeatable concern and attaching it uniformly across your component tree.
Practical HOC Patterns
Higher-order components shine in a handful of recurring scenarios. In my time writing React, these are the patterns I reach for most often.
Waiting On Data With A Loader
When a component can't render until props arrive from an API call, a common approach is to sprinkle loading logic throughout the component itself. It works, but it's messy. A cleaner solution is a dedicated HOC that inspects the props and shows a loading state until they're ready.
To see this in action, let's build a list of public API categories. First, generate a new React app:
npx create-react-app repos-list
The basic list component looks like this:
//List.js
import React from 'react';
const List = (props) => {
const { repos } = props;
if (!repos) return null;
if (!repos.length) return <p>No repos, sorry</p>;
return (
<ul>
{repos.map((repo) => {
return <li key={repo.id}>{repo.full_name}</li>;
})}
</ul>
);
};
export default List;
Breaking that down, we initialize a functional component named List and pass props to it:
const List = (props) => {};
Next, a constant named repos is created and passed via props so it can drive what renders:
const { repos } = props;
The component returns null when fetching is done but repos is still empty. A conditional render shows "No repos, sorry" if the array has no length:
if (!repos) return null;
if (!repos.length) return <p>No repos, sorry</p>;
Finally, we map through repos, rendering each full name with a unique key:
return (
<ul>
{repos.map((repo) => {
return <li key={repo.id}>{repo.full_name}</li>;
})}
</ul>
);
Now the loader HOC that keeps users informed:
//withdLoading.js
import React from 'react';
function WithLoading(Component) {
return function WihLoadingComponent({ isLoading, ...props }) {
if (!isLoading) return <Component {...props} />;
return <p>Hold on, fetching data might take some time.</p>;
};
}
export default WithLoading;
That HOC renders "Hold on, fetching data might take some time" while the app is still waiting. It relies on an isLoading prop to decide what to show. In App.js, the loading logic stays in the HOC instead of the List component:
import React from 'react';
import List from './components/List.js';
import WithLoading from './components/withLoading.js';
const ListWithLoading = WithLoading(List);
class App extends React.Component {
state = {
{
};
componentDidMount() {
this.setState({ loading: true });
fetch(`https://api.github.com/users/hacktivist123/repos`)
.then((json) => json.json())
.then((repos) => {
this.setState({ loading: false, repos: repos });
});
}
render() {
return (
<ListWithLoading
isLoading={this.state.loading}
repos={this.state.repos}
/>
);
}
}
export default App;
The whole app wires together like this:
class App extends React.Component {
state = {
loading: false,
repos: null,
};
componentDidMount() {
this.setState({ loading: true });
fetch(`https://api.github.com/users/hacktivist123/repos`)
.then((json) => json.json())
.then((repos) => {
this.setState({ loading: false, repos: repos });
});
}
It's a class component named App() with state for loading (initially false) and repos (initially null). On mount, loading flips to true and a fetch request goes out. When the request resolves, loading is set to false and the data populates repos.
const ListWithLoading = WithLoading(List);
The new ListWithLoading component composes the WithLoading HOC around List:
render() {
return (
<ListWithLoading
isLoading={this.state.loading}
repos={this.state.repos}
/>
);
}
That component receives both the loading and repos state values as props. While data is in flight, the HOC shows its loading text:
Once loading completes and props are populated, the repositories render:
Guarding Protected Routes
For a component that only authenticated users should see, a withAuth() HOC handles the check. It wraps the protected component and decides what to render based on auth state.
Here's the basic withAuth HOC:
// withAuth.js
import React from "react";
export function withAuth(Component) {
return class AuthenticatedComponent extends React.Component {
isAuthenticated() {
return this.props.isAuthenticated;
}
/**
* Render
*/
render() {
const loginErrorMessage = (
<div>
Please <a href="https://www.smashingmagazine.com/login">login</a> in order to view this part of the application.
</div>
);
return (
<div>
{ this.isAuthenticated === true ? <Component {...this.props} /> : loginErrorMessage }
</div>
);
}
};
}
export default withAuth;
This HOC takes a component and returns a new AuthenticatedComponent. If the user isn't authenticated, it renders loginErrorMessage; otherwise it renders the wrapped component. Note that this.props.isAuthenticated must come from your app's logic or, alternatively, via react-redux from global state.
Using it on a protected component is straightforward:
// MyProtectedComponent.js
import React from "react";
import {withAuth} from "./withAuth.js";
export class MyProectedComponent extends React.Component {
/**
* Render
*/
render() {
return (
<div>
This is only viewable by authenticated users.
</div>
);
}
}
// Now wrap MyPrivateComponent with the requireAuthentication function
export default withAuth(MyPrivateComponent);
The component is now only viewable by authenticated users.
Injecting Styles
When multiple places need the same styling — a backgroundColor or fontSize, for instance — a HOC can supply those props with the appropriate className, sparing you from repeating the CSS wiring.
Take a minimal component that renders "hello" plus a person's name from a name prop:
// A simple component
const HelloComponent = ({ name, ...otherProps }) => (
<div {...otherProps}>Hello {name}!/div>
);
A withStyling HOC can add the styling to that text:
const withStyling = (BaseComponent) => (props) => (
<BaseComponent {...props} style={{ fontWeight: 700, color: 'green' }} />
);
Wrap the HOC around the component, creating a pure component named EnhancedHello:
const EnhancedHello = withStyling(HelloComponent);
Rendering EnhancedHello applies the injected styles:
<EnhancedHello name='World' />
The output now carries the styling from the HOC:
<div style={{fontWeight: 700, color: 'green' }}>Hello World</div>
Supplying Reusable Props
Another frequent use case is identifying a prop needed across many components and handing it to them through a HOC. Reusing the earlier example:
// A simple component
const HelloComponent = ({ name, ...otherProps }) => (
<div {...otherProps}>Hello {name}!</div>
);
The withNameChange HOC sets a name prop on its wrapped component to "New Name":
const withNameChange = (BaseComponent) => (props) => (
<BaseComponent {...props} name='New Name' />
);
Wrap it around HelloComponent, creating EnhancedHello2:
const EnhancedHello2 = withNameChange(HelloComponent);
Rendering that enhanced component shows the new name:
<EnhancedHello />
The output reflects the injected prop value:
<div>Hello New World</div>
Changing the name prop later is just a matter of editing the HOC:
<EnhancedHello name='Shedrack' />
The rendered text follows suit:
<div>Hello Shedrack</div>
Building Your Own
Let's assemble a HOC that takes a component with a name prop and uses that prop inside the HOC. Start with a new create-react-app project:
npx create-react-app my-app
Then replace the code in index.js:
import React from 'react';
import { render } from 'react-dom';
const Hello = ({ name }) =>
<h1>
Hello {name}!
</h1>;
function withName(WrappedComponent) {
return class extends React.Component {
render() {
return <WrappedComponent name="Smashing Magazine" {...this.props} />;
}
};
}
const NewComponent = withName(Hello);
const App = () =>
<div>
<NewComponent />
</div>;
render(<App />, document.getElementById('root'));
You should see the result on screen:
Walking through the snippet:
const Hello = ({ name }) =>
<h1>
Hello {name}!
</h1>;
Here we have a functional component with a name prop, rendering "Hello" and the prop value inside an h1:
function withName(WrappedComponent) {
return class extends React.Component {
render() {
return <WrappedComponent name="Smashing Magazine" {...this.props} />;
}
};
}
That's the HOC, withName(). It returns an anonymous class component that renders the wrapped component and assigns a value to its name prop:
const NewComponent = withName(Hello);
We create NewComponent by applying the HOC to our original functional component, hello:
const App = () =>
<div>
<NewComponent />
</div>;
render(<App />, document.getElementById('root'));
Finally, an App functional component renders NewComponent inside a div, and render from react-dom puts it in the browser. That's all it takes — withName takes a component and returns an enhanced one. Months down the line, changes require a single edit to the HOC instead of touching every consumer.
Wrapping Up
These patterns cover the most common reasons to reach for higher-order components in React. For a deeper dive, the following references are worth reading:
- "Higher-Order Functions", Eloquent JavaScript, Marijn Haverbeke
- "Introduction to Higher-Order Components (HOCs) in React", Johnson Ogwuru
- "React Higher-Order Components", Tyler McGinnis
- "Simple Explanation of Higher-Order Components (HOCs)", Jakob Lind
- "A Quick Intro to React's Higher-Order Components", Patrick Moriarty, Alligator.io
- "Higher-Order Functions in JavaScript", Zslot Nagy




