Advanced Component Composition in React: Patterns and Best Practices
Learn the essential skills and steps to become a full stack developer. Start your journey today with this comprehensive guide for beginners!
Last Update: 15 Oct 2024

Advanced Component Composition in React: Patterns and Best Practices
In React development, understanding advanced component composition patterns can elevate your ability to build flexible, reusable, and scalable applications. This blog will cover three powerful composition patterns: Compound Components, Render Props, and Higher-Order Components (HOCs).
By the end, you’ll know when and how to use these patterns to solve complex UI challenges in a maintainable and elegant way.
1. Compound Components: Building a Flexible API
Compound components allow related components to share state and behavior in a flexible, decoupled way. These components work together like a team, allowing for a more declarative API, perfect for building reusable libraries like dropdowns, tabs, or modals.
Step 1: Creating the Parent Component
Let's build a simple Accordion using compound components. The parent component will manage the state and share it with the children.
import React, { useState, createContext, useContext } from 'react';
const AccordionContext = createContext();
const Accordion = ({ children }) => {
const [openIndex, setOpenIndex] = useState(null);
const toggleIndex = (index) => {
setOpenIndex(index === openIndex ? null : index);
};
return (
<AccordionContext.Provider value={{ openIndex, toggleIndex }}>
<div className="accordion">{children}</div>
</AccordionContext.Provider>
);
};
Step 2: Creating Child Components
The AccordionItem
, AccordionHeader
, and AccordionBody
components will consume the context to know when to expand or collapse.
const AccordionItem = ({ index, children }) => {
const { openIndex } = useContext(AccordionContext);
return <div>{openIndex === index && children}</div>;
};
const AccordionHeader = ({ index, children }) => {
const { toggleIndex } = useContext(AccordionContext);
return <h2 onClick={() => toggleIndex(index)}>{children}</h2>;
};
const AccordionBody = ({ children }) => {
return <div>{children}</div>;
};
Step 3: Using the Compound Components
Now, use the compound components with a clean and intuitive API.
const App = () => {
return (
<Accordion>
<AccordionHeader index={0}>Section 1</AccordionHeader>
<AccordionItem index={0}>
<AccordionBody>Content for section 1</AccordionBody>
</AccordionItem>
<AccordionHeader index={1}>Section 2</AccordionHeader>
<AccordionItem index={1}>
<AccordionBody>Content for section 2</AccordionBody>
</AccordionItem>
</Accordion>
);
};
Why Use Compound Components?
- Decoupled structure: Parent and child components are more flexible and easier to manage.
- Flexible API: Consumers can easily compose and arrange the UI without coupling logic.
2. Render Props: Sharing Behavior Across Components
Render Props is a pattern where a function is passed as a prop to a component, allowing it to dynamically render based on internal state or behavior. It’s useful when different components need to share state or behavior but need different UIs.
Step 1: Creating a Component with a Render Prop
Let's build a MouseTracker
component that tracks the mouse position and shares the state with a child component via a render prop.
import React, { useState } from 'react';
const MouseTracker = ({ render }) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(mousePosition)}</div>;
};
Step 2: Using the Render Prop Component
Pass a function to the render
prop to control what’s displayed when the mouse moves.
const App = () => {
return (
<div>
<h1>Move your mouse around!</h1>
<MouseTracker
render={({ x, y }) => (
<p>
The mouse position is ({x}, {y})
</p>
)}
/>
</div>
);
};
Why Use Render Props?
- Code reuse: Share common logic between components while keeping the rendering logic separate.
- Customization: Render prop functions can completely control the output, making this a highly flexible pattern.
3. Higher-Order Components (HOCs): Reusing Logic Across Multiple Components
Higher-Order Components (HOCs) are functions that take a component and return a new component with enhanced behavior or additional props. This pattern is great for code reuse, particularly for things like authentication, logging, or theming.
Step 1: Creating a Higher-Order Component
Let’s build a simple HOC called withLogger
that logs when a component mounts.
import React, { useEffect } from 'react';
const withLogger = (WrappedComponent) => {
return (props) => {
useEffect(() => {
console.log(`Component ${WrappedComponent.name} mounted.`);
}, []);
return <WrappedComponent {...props} />;
};
};
Step 2: Using the HOC
Wrap any component you want with withLogger
to get the logging functionality.
const MyComponent = () => {
return <div>Hello, I’m a component!</div>;
};
const MyComponentWithLogger = withLogger(MyComponent);
const App = () => {
return <MyComponentWithLogger />;
};
Why Use HOCs?
- Separation of concerns: HOCs encapsulate cross-cutting concerns like logging, authentication, or theming without modifying the original component.
- Reusability: The same HOC can be applied to many components, reducing code duplication.
Conclusion
Understanding and applying these advanced component composition patterns—Compound Components, Render Props, and Higher-Order Components (HOCs)—can make your React applications much more flexible and maintainable.
- Compound Components are great for building reusable UI libraries.
- Render Props offer a flexible way to share behavior across components.
- HOCs allow you to reuse logic across multiple components in a declarative manner.
Mastering these patterns will enable you to write React components that are both easy to use and highly reusable across different parts of your application.
Trendingblogs
Get the best of our content straight to your inbox!
By submitting, you agree to our privacy policy.