In my eight years of building React applications, I have seen many developers struggle with “prop drilling.”
It usually starts small but quickly turns into a maintenance nightmare where data is passed through ten levels of components.
I remember working on a large-scale fintech dashboard for a client in New York where we hit this exact wall.
The solution wasn’t a complex state management library, but a fundamental shift toward component composition.
In this tutorial, I will show you exactly how to master React component composition patterns to keep your code clean and scalable.
What is Component Composition in React?
Component composition is a design pattern where you combine smaller, independent components to build more complex UIs.
Instead of creating massive components that try to do everything, you create specialized pieces that fit together.
Think of it like building a custom PC where you choose the specific GPU, RAM, and Motherboard that fit your needs.
In React, this is often preferred over inheritance because it provides much more flexibility in how components share logic and UI.
Method 1: Use the “Containment” Pattern (Children Prop)
The simplest form of composition is containment. This is where a component doesn’t know its children ahead of time.
I often use this for generic containers like Modals, Sidebars, or Card layouts used in e-commerce apps.
In this example, we will build a Professional Service Card used by a consulting firm based in Chicago.
import React from 'react';
// The Container Component
const ServiceCard = ({ children, title }) => {
return (
<div style={{
border: '1px solid #e0e0e0',
borderRadius: '8px',
padding: '20px',
margin: '10px',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)',
maxWidth: '400px'
}}>
<h2 style={{ color: '#004a99', borderBottom: '2px solid #f2f2f2' }}>{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
};
// Implementation
const ConsultingDashboard = () => {
return (
<div style={{ display: 'flex', flexWrap: 'wrap' }}>
<ServiceCard title="Tax Strategy - FY2026">
<p>Our Wall Street experts provide quarterly tax optimization for small businesses.</p>
<button style={{ backgroundColor: '#004a99', color: 'white', padding: '10px' }}>
Schedule Consultation
</button>
</ServiceCard>
<ServiceCard title="Retirement Planning">
<ul>
<li>401(k) Management</li>
<li>IRA Rollovers</li>
<li>Estate Planning</li>
</ul>
</ServiceCard>
</div>
);
};
export default ConsultingDashboard;You can see the output in the screenshot below.

As you can see, the ServiceCard doesn’t care if it contains a list, a button, or just text.
This makes the component highly reusable across different parts of the application without adding dozens of props.
Method 2: The “Slots” Pattern for Complex Layouts
Sometimes, passing a single children prop isn’t enough. You might need to place elements in specific locations.
I call this the “Slot” pattern. It is incredibly useful for headers or navigation bars in enterprise-level apps.
Let’s look at a Healthcare Provider Portal header designed for a clinic in Houston.
import React from 'react';
// Layout component with multiple slots
const LayoutHeader = ({ leftSlot, centerSlot, rightSlot }) => {
return (
<header style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '15px 30px',
backgroundColor: '#ffffff',
borderBottom: '1px solid #d1d1d1'
}}>
<div className="header-left">{leftSlot}</div>
<div className="header-center">{centerSlot}</div>
<div className="header-right">{rightSlot}</div>
</header>
);
};
// Implementation
const MedicalPortal = () => {
return (
<LayoutHeader
leftSlot={
<img src="https://via.placeholder.com/150x40?text=HealthCare+Plus" alt="Logo" />
}
centerSlot={
<input
type="text"
placeholder="Search Patient Records (SSN or Name)..."
style={{ width: '400px', padding: '8px' }}
/>
}
rightSlot={
<div style={{ display: 'flex', alignItems: 'center', gap: '15px' }}>
<span>Welcome, Dr. Smith</span>
<button style={{ padding: '8px 15px' }}>Logout</button>
</div>
}
/>
);
};
export default MedicalPortal;You can see the output in the screenshot below.

By using props as “slots,” I avoided passing specific data strings and instead passed fully formed components.
This keeps the LayoutHeader clean and prevents it from becoming a “God component” that knows too much about business logic.
Method 3: Compound Components Pattern
This is my favorite pattern for building complex UI components like Tabs, Select menus, or Accordions.
It allows a group of components to share an internal state implicitly while giving the user full control over the DOM structure.
Let’s build a Real Estate Listing filter for a property management company in California.
import React, { useState, createContext, useContext } from 'react';
// 1. Create a Context
const FilterContext = createContext();
// 2. Parent Component
const PropertyFilter = ({ children, onFilterChange }) => {
const [activeFilter, setActiveFilter] = useState('All');
const toggleFilter = (filter) => {
setActiveFilter(filter);
onFilterChange(filter);
};
return (
<FilterContext.Provider value={{ activeFilter, toggleFilter }}>
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
{children}
</div>
</FilterContext.Provider>
);
};
// 3. Child Components
const FilterButton = ({ label }) => {
const { activeFilter, toggleFilter } = useContext(FilterContext);
const isActive = activeFilter === label;
return (
<button
onClick={() => toggleFilter(label)}
style={{
padding: '10px 20px',
backgroundColor: isActive ? '#2c3e50' : '#ecf0f1',
color: isActive ? 'white' : 'black',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
{label}
</button>
);
};
// 4. Using the Compound Components
const CaliforniaRealEstate = () => {
const handleFilter = (val) => console.log(`Filtering by: ${val}`);
return (
<div style={{ padding: '40px' }}>
<h1>Available Properties in San Francisco</h1>
<PropertyFilter onFilterChange={handleFilter}>
<FilterButton label="All" />
<FilterButton label="Single Family" />
<FilterButton label="Condo" />
<FilterButton label="Townhouse" />
</PropertyFilter>
<div className="results">
<p>Showing results for Bay Area residential listings...</p>
</div>
</div>
);
};
export default CaliforniaRealEstate;You can see the output in the screenshot below.

In this setup, the FilterButton components are connected to the PropertyFilter without explicitly passing props to each one.
It provides a very clean API for other developers who will use your components.
Method 4: Specialization Composition
Sometimes you have a general component, and you want to create a “special” version of it.
Instead of adding a type=”success” prop and using many if statements, you can use composition to specialize it.
Let’s look at an American Logistics tracking notification system.
import React from 'react';
// The General Component
const Notification = ({ message, type, icon }) => {
const getColors = () => {
if (type === 'alert') return { bg: '#fff3cd', border: '#ffeeba', color: '#856404' };
return { bg: '#d4edda', border: '#c3e6cb', color: '#155724' };
};
const colors = getColors();
return (
<div style={{
padding: '15px',
margin: '10px 0',
borderRadius: '4px',
border: `1px solid ${colors.border}`,
backgroundColor: colors.bg,
color: colors.color,
display: 'flex',
alignItems: 'center'
}}>
<span style={{ marginRight: '10px' }}>{icon}</span>
{message}
</div>
);
};
// Specialized Components
const DeliverySuccess = ({ location }) => (
<Notification
type="success"
icon="✅"
message={`Package successfully delivered to ${location}, USA.`}
/>
);
const DelayAlert = ({ days }) => (
<Notification
type="alert"
icon="⚠️"
message={`Weather delay in the Midwest. Expected delay: ${days} business days.`}
/>
);
// Implementation
const LogisticsDashboard = () => {
return (
<div style={{ padding: '20px' }}>
<h3>FedEx/UPS Tracking Updates</h3>
<DeliverySuccess location="Phoenix, AZ" />
<DelayAlert days={2} />
</div>
);
};
export default LogisticsDashboard;This makes the code much more readable. When I see <DeliverySuccess />, I know exactly what it does without checking the props.
I hope this tutorial helped you understand the power of React component composition.
Once you start thinking in terms of “How can I compose this?” instead of “How many props do I need?”, your code quality will improve drastically.
I have found that using these patterns makes my applications much easier to test and maintain over the years.
You may also like to read:
- How to Create a Reusable Button Component in React
- React Functional Component Unmount
- Chakra UI React Component Library
- How to Use HeroUI (Formerly NextUI) in React

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.