Compound Components in React: What, Why, When, and How

1. What the heck is a Compound Component? A Compound Component is a design pattern where you group multiple smaller components together to work as a team, while each part stays flexible and independent. In React, you use components like: Open Edit Delete Without this pattern, you'd end up with: Prop drilling – passing data through 5 levels just to get it to the right spot. Giant, rigid components – one massive component with 20 props to handle every possible case. Hard-to-read code – no one want
1. What the heck is a Compound Component?
A Compound Component is a design pattern where you group multiple smaller components together to work as a team, while each part stays flexible and independent.
In React, you use components like:
<Dropdown>
<Dropdown.Toggle>Open</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item>Edit</Dropdown.Item>
<Dropdown.Item>Delete</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
Enter fullscreen mode Exit fullscreen mode
2. Why should you care?
Without this pattern, you'd end up with:
- Prop drilling – passing data through 5 levels just to get it to the right spot.
- Giant, rigid components – one massive component with 20 props to handle every possible case.
- Hard-to-read code – no one wants to read a wall of props.
Compound components give you:
- Flexibility – users can add, remove, or reorder pieces.
- Shared state – all pieces can access the same data (like isOpen in a dropdown) without passing props manually.
- Clean, readable JSX – looks like HTML, works like magic.
3. When should you use it? (How to know it's time)
Multiple parts always appear together
Parts share state
Users need to customize layout
4. How to build one? (Example: A Product Card)
import React, { createContext, useContext } from "react";
const ProductContext = createContext();
const ProductCard = ({ product, children }) => {
return (
<ProductContext.Provider value={{ product }}>
<div className="product-card">{children}</div>
</ProductContext.Provider>
);
};
const Image = () => {
const { product } = useContext(ProductContext);
return product?.image ? <img src={product.image} /> : null;
};
const Title = () => {
const { product } = useContext(ProductContext);
return product?.title ? <h3>{product.title}</h3> : null;
};
const Price = () => {
const { product } = useContext(ProductContext);
return product?.price ? <p>${product.price}</p> : null;
};
const AddToCart = () => {
const { product } = useContext(ProductContext);
return <button onClick={() => alert(`Added ${product.title}`)}>Add to Cart</button>;
};
ProductCard.Image = Image;
ProductCard.Title = Title;
ProductCard.Price = Price;
ProductCard.AddToCart = AddToCart;
Enter fullscreen mode Exit fullscreen mode
On homepage (simple):
<ProductCard product={product}>
<ProductCard.Image />
<ProductCard.Title />
</ProductCard>
Enter fullscreen mode Exit fullscreen mode
On wishlist (no price, no cart button):
<ProductCard product={product}>
<ProductCard.Image />
<ProductCard.Title />
</ProductCard>
Enter fullscreen mode Exit fullscreen mode
Rearrange the order:
<ProductCard product={product}>
<ProductCard.Price /> {/* Price first */}
<ProductCard.Title /> {/* Title second */}
<ProductCard.Image /> {/* Image last */}
</ProductCard>
Enter fullscreen mode Exit fullscreen mode



