Skip to content

5 Must-Know React Component Patterns for Scalable Code

Five React component patterns for reusable code, with examples: containers, HOCs, render props, compound components and custom hooks.
TipUpdated 3 min readDevelopmentJavascriptEs6React Js

When working with React, especially on larger or long-term projects, it's easy to end up with messy, repetitive, or hard-to-maintain code. But React gives us patterns (simple structures and ideas) that make our code easier to read, reuse, and scale.

In this article, we’ll explore 5 React component patterns every developer should know. These aren’t frameworks or libraries. They’re smart ways of organising your code that work beautifully with React’s component-based structure.

1. Presentational vs Container Components

One of the easiest and most powerful ideas to start with is splitting your components into two types:

  • Presentational Components: Focus on how things look (UI), are stateless, and receive data via props.
  • Container Components: Handle state, logic, and data. Focus on how things work.
// Presentational Component
const UserCard = ({ name, onClick }) => (
  <div onClick={onClick}>
    <h2>{name}</h2>
  </div>
);

// Container Component
const UserContainer = () => {
  const name = "Ali";

  const handleClick = () => alert(`Hi, ${name}`);

  return <UserCard name={name} onClick={handleClick} />;
};

This pattern helps you reuse UI components and keep your code clean and testable.

2. Higher-Order Components (HOC)

A Higher-Order Component is a function that takes a component and returns a new one, with extra features or props.

Commonly used in libraries like React Redux and React Router.

const withLoading = (Component) => {
  return function WithLoadingComponent({ loading, ...props }) {
    if (loading) return <p>Loading...</p>;
    return <Component {...props} />;
  };
};

// Usage
const UserList = ({ users }) => (
  <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>
);

const UserListWithLoading = withLoading(UserList);

3. Render Props

A Render Prop is a function prop that a component uses to know what to render.

const MouseTracker = ({ render }) => {
  const [pos, setPos] = React.useState({ x: 0, y: 0 });

  const handleMove = (e) => setPos({ x: e.clientX, y: e.clientY });

  return <div onMouseMove={handleMove}>{render(pos)}</div>;
};

// Usage
<MouseTracker render={(pos) => <p>Mouse at {pos.x}, {pos.y}</p>} />;

This gives full flexibility over how the component displays data.

4. Compound Components

Compound components allow you to build related components that work together and share internal state.

const Tabs = ({ children }) => {
  const [activeIndex, setActiveIndex] = React.useState(0);

  return React.Children.map(children, (child, index) =>
    React.cloneElement(child, {
      isActive: index === activeIndex,
      onClick: () => setActiveIndex(index),
    })
  );
};

const Tab = ({ label, isActive, onClick }) => (
  <button onClick={onClick} style={{ fontWeight: isActive ? "bold" : "normal" }}>
    {label}
  </button>
);

// Usage
<Tabs>
  <Tab label="Tab 1" />
  <Tab label="Tab 2" />
</Tabs>

This is perfect for things like tabs, modals, or dropdown menus.

5. Custom Hooks

Custom hooks let you extract logic into reusable functions using useState, useEffect, etc.

function useToggle(initial = false) {
  const [value, setValue] = React.useState(initial);
  const toggle = () => setValue((v) => !v);
  return [value, toggle];
}

// Usage
const Switch = () => {
  const [isOn, toggle] = useToggle();
  return <button onClick={toggle}>{isOn ? "ON" : "OFF"}</button>;
};

This keeps components clean and promotes DRY code.

Why These Patterns Matter

  • Scalable architecture
  • Reusable logic and components
  • Cleaner, testable code
  • Easier team collaboration

These patterns make a real difference when working on real-world projects, from solo builds to large enterprise apps.

Trade-offs at a Glance

Every pattern solves one problem and introduces another. This is the short version:

PatternStrengthWatch out for
Presentational vs ContainerLogic and UI are separate, so each is easier to test and reuseMore components and some boilerplate
Higher-Order ComponentsAdds the same behaviour to many components without changing themLayers of wrappers make debugging harder, and props can clash
Render PropsFull control over what gets rendered with shared logicDeep nesting that becomes hard to read
Compound ComponentsA clean API for related pieces such as tabs or dropdowns, without prop drillingHarder to build correctly
Custom HooksReusable stateful logic that keeps components smallToo many tiny hooks can scatter logic across files

Here is a slightly bigger custom hook than the toggle above, for loading data:

function useFetch(url) {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    let cancelled = false;
    fetch(url)
      .then((res) => res.json())
      .then((json) => {
        if (!cancelled) {
          setData(json);
          setLoading(false);
        }
      });
    return () => {
      cancelled = true;
    };
  }, [url]);

  return { data, loading };
}

Related Post

New to React? Make sure you've mastered the basics: 7 JavaScript Concepts to Learn Before React

FAQs

Q1: Are these official React patterns?

No, but they’re widely accepted best practices in the React community.

Q2: Should I use all these patterns in one project?

Use the ones that fit your app’s needs. The goal is clarity and reusability.

Q3: Are hooks better than HOCs or render props?

Hooks simplify a lot of things, but each pattern has its use case.

Q4: What’s the easiest pattern to start with?

Start with Presentational vs Container: it’s simple and effective.

Q5: Are HOCs still relevant?

Yes, especially in older codebases and some libraries, but for new code custom hooks usually do the same job with less wrapping.

Q6: Custom hook or render prop?

Use a custom hook to share stateful logic. Use a render prop when the caller needs to decide what gets rendered with that logic.

Conclusion

React is powerful, but using the right patterns can make it even better.

By applying these 5 component patterns, you’ll write code that’s:

  • Smarter
  • Easier to test
  • Ready to scale

Save this post. Bookmark it. And share it with your dev friends!

Want more? Browse more tutorials at bilalshafqat.com

Share this
Build work

Need this built properly?

I build the web and mobile products these notes come out of, in React, React Native and Node. A first conversation costs nothing and often ends with a smaller scope than you expected.

Not ready to book a call? Get a free website health check