Compound Components: Flexible APIs Built from Small Pieces
Create components that work together via context so consumers can compose them in any order.
Introduction
Compound components are a group of components designed to work together—like Tabs, Tabs.List, and Tabs.Panel. They share context so consumers can compose them in whatever order they need.
Why this matters
Sometimes a single component needs flexible “slots” that consumers can arrange. Tabs, cards, dropdowns—these benefit from a parent component that provides context and child sub‑components that read from it.
The problem
Hard‑coding section order or passing lots of props between siblings leads to rigid, brittle APIs.
Inefficient approach
One monolithic component controls layout and content—no flexibility:
function PostCard({ post }) {
return (
<div className="post-card">
<h2>{post.title}</h2>
<p>{post.content}</p>
<p>— {post.author}</p>
</div>
);
}
function App() {
const post = {
title: "Understanding React Patterns",
content: "React patterns help us write better, more maintainable code...",
author: "John Doe"
};
return (
<div>
<h1>Compound Components Pattern</h1>
<PostCard post={post} />
</div>
);
}
export default App;
The solution
Provide shared data via context and expose sub‑components as static properties.
import { createContext, useContext } from "react";
const PostContext = createContext();
const usePost = () => useContext(PostContext);
function PostCard({ children, post }) {
return (
<PostContext.Provider value={post}>
<div className="post-card">{children}</div>
</PostContext.Provider>
);
}
PostCard.Header = function PostCardHeader() {
const post = usePost();
return <h2>{post.title}</h2>;
};
PostCard.Body = function PostCardBody() {
const post = usePost();
return <p>{post.content}</p>;
};
PostCard.Footer = function PostCardFooter() {
const post = usePost();
return <p>— {post.author}</p>;
};
function App() {
const post = {
title: "Understanding React Patterns",
content: "React patterns help us write better, more maintainable code...",
author: "John Doe"
};
return (
<div>
<h1>Compound Components Pattern</h1>
<PostCard post={post}>
<PostCard.Header />
<PostCard.Body />
<PostCard.Footer />
</PostCard>
</div>
);
}
export default App;
Step-by-step
- Create a context for shared data and a
usePosthelper. - Implement
PostCardto provide thepostvia context. - Add static sub‑components (
Header,Body,Footer) that read from context. - Compose sub‑components in any order inside
PostCard. - Keep sub‑components focused on rendering; avoid state in them.
Tips
- Keep sub‑components small and focused; they should read from context, not manage state.
- Document which sub‑components are available and any required order (ideally none).
- For advanced needs (controlled vs uncontrolled), consider exposing both stateful and stateless variants.
Knowledge base
Problem snippet:
<h2>{post.title}</h2>
<p>{post.content}</p>
<p>— {post.author}</p>
Solution snippet:
<PostCard post={post}>
<PostCard.Header />
<PostCard.Body />
<PostCard.Footer />
</PostCard>