What are higher-order components in React?
TL;DR
Higher-order components (HOCs) in React are functions that take a component and return a new component with additional props or behavior. They are used to reuse component logic. For example, if you have a component MyComponent, you can create an HOC like this:
const withExtraProps = (WrappedComponent) => {return (props) => <WrappedComponent {...props} extraProp="value" />;};const EnhancedComponent = withExtraProps(MyComponent);
HOCs were especially common before Hooks and remain valid in existing code and library APIs such as React Redux's connect. For new function components, custom Hooks usually share stateful logic with less wrapper nesting. An HOC can still be appropriate when the abstraction must return a component or wrap rendering rather than only share logic.
What are higher-order components in React?
HOCs are component-transforming functions: they accept one component type and return another that adds or coordinates behavior.
Definition
Higher-order components (HOCs) are functions in React that take a component as an argument and return a new component. The new component typically wraps the original component and adds additional props, state, or behavior. HOCs are a pattern for reusing component logic.
The current React learning material emphasizes custom Hooks for sharing stateful logic. HOCs are still a valid composition pattern, but they are most common in older codebases and library APIs that need to enhance or wrap a component.
Purpose
HOCs are used to:
- Share common functionality between components
- Abstract and reuse component logic
- Enhance components with additional props or state
Example
Here is a simple example of an HOC that adds an extraProp to a wrapped component:
// Define the HOCconst withExtraProps = (WrappedComponent) => {const ComponentWithExtraProps = (props) => {return <WrappedComponent {...props} extraProp="value" />;};// Set a useful displayName for debugging in React DevToolsconst wrappedName =WrappedComponent.displayName || WrappedComponent.name || 'Component';ComponentWithExtraProps.displayName = `withExtraProps(${wrappedName})`;return ComponentWithExtraProps;};// Define a component to be wrappedconst MyComponent = (props) => {return <div>{props.extraProp}</div>;};// Wrap the component using the HOCconst EnhancedComponent = withExtraProps(MyComponent);// Use the enhanced componentconst App = () => {return <EnhancedComponent />;};export default App;
In this example, withExtraProps is an HOC that adds an extraProp to MyComponent. The EnhancedComponent now has access to extraProp. The displayName convention withExtraProps(MyComponent) makes the component identifiable in React DevTools.
Common use cases
HOCs remain most relevant where an API needs to wrap rendering or enhance a component type:
- Authentication: Wrapping components to check if a user is authenticated before rendering.
- Logging: Adding logging functionality to components.
- Theming: Injecting theme-related props into components.
- Data fetching: Fetching data and passing it as props to components.
Best practices
An HOC should preserve the wrapped component's identity contract and make its injected behavior explicit:
- Do not mutate the original component: Always return a new component.
- Do not create HOCs inside the render method: Calling
withExtraProps(MyComponent)inside another component's render produces a brand-new component type on every render, so React unmounts and remounts the subtree, losing all state and DOM. Apply HOCs at the module level. - Set a
displayName: Wrap the inner name asHOCName(WrappedName)so React DevTools shows something useful instead ofAnonymous. - Watch for prop-name collisions: An HOC that injects, say, an
extraPropwill silently overwrite anyextraPropthe caller passes in (or vice versa, depending on spread order). Namespace injected props or document them clearly. - Forward refs explicitly: Wrapping a component in an HOC means a
refpassed by the consumer lands on the wrapper, not the inner component. In React 19,forwardRefis no longer required andrefis now a regular prop on function components, so the simplest fix is to acceptrefas a prop and forward it:<WrappedComponent {...props} ref={props.ref} extraProp="value" />. For pre-19 codebases, you'd useReact.forwardRefinside the HOC. - Use HOCs sparingly: Overusing HOCs can make the code harder to understand and creates deeply nested wrapper trees ("wrapper hell"). Custom hooks usually compose more cleanly.
Alternatives
Modern React offers other reuse patterns that avoid adding a wrapper component when wrapping is unnecessary:
- Custom hooks: Custom hooks share stateful logic between function components without adding wrapper components to the tree and are usually the simpler option for new code.
- Render props: A pattern where a component uses a function as a prop to determine what to render.