React Design Pattern /HOC Pattern

Published: (December 25, 2025 at 08:05 PM EST)
5 min read
Source: Dev.to

Source: Dev.to

Higher‑Order Component (HOC) Overview

A Higher‑Order Component (HOC) is a function that receives a component as an argument, adds some logic, and returns a new component that incorporates that logic.
HOCs let you reuse behavior across many components without repeating code.

1️⃣ Adding Styles with an HOC

Instead of creating a style object in every component, we can create an HOC that injects the same styles into any component we pass to it.

// withStyles.tsx
function withStyles(Component: React.ComponentType) {
  return (props: T) => {
    const style = { padding: '0.6rem', margin: '4rem' };
    return ;
  };
}

Usage

const Button = () => Click me!;
const Text   = () => 
Hello World!
;

const StyledButton = withStyles(Button);
const StyledText   = withStyles(Text);

StyledButton and StyledText are the original components enhanced with the style prop supplied by withStyles.

2️⃣ Adding a Loading Indicator with an HOC

When fetching data we often want to show a Loading… screen until the request resolves.
Rather than sprinkling loading logic throughout many components, we can encapsulate it in an HOC called withLoader.

2.1 Bare‑minimum skeleton

function withLoader(
  Element: React.ComponentType,
  url: string
) {
  return (props: Omit) => {
    // implementation will go here
    return ;
  };
}

2.2 Full implementation

import { useEffect, useState } from 'react';

function withLoader(
  Element: React.ComponentType,
  url: string
) {
  return (props: Omit) => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      fetch(url)
        .then((res) => res.json())
        .then((json) => {
          setData(json);
          setLoading(false);
        })
        .catch((err) => {
          console.error(err);
          setLoading(false);
        });
    }, [url]);

    if (loading) {
      return Loading…;
    }

    // Pass the fetched data as a `data` prop to the wrapped component
    return ;
  };
}

2.3 Applying the HOC to a component

// CatImages.tsx
type CatImagesProps = {
  data: any; // replace `any` with a proper type if you have one
};

function CatImages({ data }: CatImagesProps) {
  return (
    

      {data.map((cat: any) => (
        
          
        
      ))}
    

  );
}

// Export the wrapped component
export default withLoader(CatImages, 'https://api.thecatapi.com/v1/images/search?limit=10');

Now CatImages receives a data prop automatically, and while the request is pending the user sees a Loading… message.

3️⃣ Composing Multiple HOCs

You can stack HOCs to combine behaviours.
Below we create a withHover HOC that supplies a hovering prop, then compose it with withLoader.

3.1 withHover HOC

import { useState, useCallback } from 'react';

function withHover(Component: React.ComponentType) {
  return (props: T) => {
    const [hovering, setHovering] = useState(false);

    const onMouseEnter = useCallback(() => setHovering(true), []);
    const onMouseLeave = useCallback(() => setHovering(false), []);

    return (
      
        
      
    );
  };
}

3.2 Compose withHover + withLoader

// WrappedCatImages.tsx
export default withHover(
  withLoader(CatImages, 'https://api.thecatapi.com/v1/images/search?limit=10')
);

Inside CatImages you can now use both data (from withLoader) and hovering (from withHover) to render a conditional “Hovering” box.

4️⃣ Replacing an HOC with a Hook (useHover)

Hooks can often achieve the same result as an HOC but with less nesting.

4.1 useHover hook

import { useRef, useState, useEffect, RefObject } from 'react';

function useHover(): [RefObject, boolean] {
  const ref = useRef(null);
  const [hovering, setHovering] = useState(false);

  useEffect(() => {
    const node = ref.current;
    if (!node) return;

    const handleMouseEnter = () => setHovering(true);
    const handleMouseLeave = () => setHovering(false);

    node.addEventListener('mouseenter', handleMouseEnter);
    node.addEventListener('mouseleave', handleMouseLeave);

    return () => {
      node.removeEventListener('mouseenter', handleMouseEnter);
      node.removeEventListener('mouseleave', handleMouseLeave);
    };
  }, []);

  return [ref, hovering];
}

4.2 Using the hook inside CatImages

// CatImagesWithHook.tsx
import { useLoader } from './useLoader'; // a hook version of the loader logic
import { useHover } from './useHover';

function CatImagesWithHook() {
  const [ref, hovering] = useHover();
  const { data, loading } = useLoader('https://api.thecatapi.com/v1/images/search?limit=10');

  if (loading) return Loading…;

  return (
    
      {hovering && Hovering}
      

        {data.map((cat: any) => (
          
            
          
        ))}
      

    
  );
}

The hook approach eliminates the extra component layers that HOCs introduce, keeping the component tree flatter.

5️⃣ When to Prefer HOCs vs. Hooks

  • HOCs are great for cross‑cutting concerns that need to work with any component type (class or function) and when you want to keep the wrapped component’s API unchanged.
  • Hooks are usually the preferred modern solution for stateful logic because they avoid the “wrapper hell” that can arise with many nested HOCs.

React Docs: “In most cases, hooks will suffice and can help reduce nesting in your tree.”

6️⃣ Example of Deeply Nested HOCs (for illustration)


  
    
      
    
  

The same behaviour could often be expressed more cleanly with a combination of hooks and context providers.

TL;DR

  • HOCs let you inject props/logic into components (withStyles, withLoader, withHover).
  • You can compose multiple HOCs to build up complex behaviour.
  • Hooks (useHover, useLoader) provide a modern, less‑nested alternative for most cases.

Feel free to copy the snippets above into your project and adapt the types as needed!

Layout Example

Hook vs. Higher‑Order Component (HOC)

Adding a hook directly to a component means we no longer have to wrap the component in an HOC.
Using HOCs makes it possible to provide the same logic to many components while keeping that logic in one place. However, hooks allow us to add custom behaviour from within the component, which could potentially increase the risk of introducing bugs compared to the HOC pattern if multiple components rely on this behaviour.

Best use‑cases for a Higher‑Order Component

  • The component can operate independently, without the additional custom logic.

Best use‑cases for Hooks

  • The behaviour is not spread throughout the application; only one or a few components use it.
  • The behaviour adds a number of properties to the component.
Back to Blog

Related posts

Read more »

React Summit 2026

!Cover image for React Summit 2026https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.a...