Introduction
Compound Components are a React design pattern where multiple related components work together as one flexible UI component. A parent component manages shared behavior or state, while its child components provide different parts of the interface. This pattern is commonly used for Tabs, Accordions, Select menus, Dialogs, and other interactive components. Compound Components make APIs readable and allow developers to control how related UI pieces are combined. In this chapter, we will solve practical questions about Compound Components in React. React js Compound Components practice questions with solutions to help you understand the concepts.
1. What are Compound Components in React (React Compound Component)?
Answer:
Compound Components are a group of related React components that work together to create a single reusable UI pattern.
For example, a Tabs component can be designed like this:
<Tabs>
<Tabs.List>
<Tabs.Tab>Profile</Tabs.Tab>
<Tabs.Tab>Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
Profile Information
</Tabs.Panel>
<Tabs.Panel>
Account Settings
</Tabs.Panel>
</Tabs>
Here:
Tabs
├── Tabs.List
├── Tabs.Tab
└── Tabs.Panel
Each component has a specific responsibility, but they work together as one Tabs system.
This pattern is useful when several UI components need to share behavior or state.
2. Why Are Compound Components Useful?
Answer:
Compound Components are useful when multiple components need to coordinate with each other.
For example, an Accordion may contain:
Accordion
├── Accordion.Item
├── Accordion.Button
└── Accordion.Panel
The parent Accordion can manage which item is open while its child components handle individual parts of the UI.
Benefits include:
- Flexible component structure
- Reusable UI patterns
- Shared state management
- Clear component relationships
- Readable JSX
- Better separation of responsibilities
Instead of creating a component with many configuration props, users can compose the related components directly.
3. How Can Context API Be Used in Compound Components?
Answer:
Context is commonly used to share state between compound components without passing props through every level.
Example:
import {
createContext,
useContext,
useState
} from "react";
const AccordionContext = createContext(null);
function Accordion({ children }) {
const [open, setOpen] = useState(null);
return (
<AccordionContext.Provider
value={{ open, setOpen }}
>
{children}
</AccordionContext.Provider>
);
}
function Item({ id, children }) {
const { open, setOpen } =
useContext(AccordionContext);
const isOpen = open === id;
return (
<div>
<button
onClick={() =>
setOpen(isOpen ? null : id)
}
>
Item {id}
</button>
{isOpen && children}
</div>
);
}
Now the parent manages the shared state, while Item reads it through Context.
4. How Can You Create a Basic Compound Accordion?
Answer:
A simple Accordion can use a parent component and child components.
import {
createContext,
useContext,
useState
} from "react";
const AccordionContext = createContext(null);
function Accordion({ children }) {
const [activeItem, setActiveItem] =
useState(null);
return (
<AccordionContext.Provider
value={{
activeItem,
setActiveItem
}}
>
{children}
</AccordionContext.Provider>
);
}
function AccordionItem({ id, title, children }) {
const {
activeItem,
setActiveItem
} = useContext(AccordionContext);
const isOpen = activeItem === id;
return (
<div>
<button
onClick={() =>
setActiveItem(
isOpen ? null : id
)
}
>
{title}
</button>
{isOpen && (
<div>
{children}
</div>
)}
</div>
);
}
Usage:
function App() {
return (
<Accordion>
<AccordionItem
id="one"
title="What is React?"
>
React is a JavaScript library for building
user interfaces.
</AccordionItem>
<AccordionItem
id="two"
title="What are Hooks?"
>
Hooks allow function components to use
React features.
</AccordionItem>
</Accordion>
);
}
The parent Accordion manages the active item, while each AccordionItem controls its own UI using the shared context.
5. How Can Compound Components Share State?
Answer:
The parent component can own the shared state and provide it to child components.
Example:
const TabsContext = createContext(null);
function Tabs({ children }) {
const [activeTab, setActiveTab] =
useState("profile");
return (
<TabsContext.Provider
value={{
activeTab,
setActiveTab
}}
>
{children}
</TabsContext.Provider>
);
}
A child can access the shared state:
function TabButton({ id, children }) {
const {
activeTab,
setActiveTab
} = useContext(TabsContext);
return (
<button
onClick={() => setActiveTab(id)}
aria-selected={activeTab === id}
>
{children}
</button>
);
}
Another child can use the same state:
function TabPanel({ id, children }) {
const { activeTab } =
useContext(TabsContext);
if (activeTab !== id) {
return null;
}
return <div>{children}</div>;
}
Both components coordinate through the same Context value.
6. How Can You Build a Compound Tabs Component?
Answer:
A Tabs component is a common real-world example of Compound Components.
import {
createContext,
useContext,
useState
} from "react";
const TabsContext = createContext(null);
function Tabs({ children }) {
const [activeTab, setActiveTab] =
useState("profile");
return (
<TabsContext.Provider
value={{
activeTab,
setActiveTab
}}
>
{children}
</TabsContext.Provider>
);
}
function Tab({ id, children }) {
const {
activeTab,
setActiveTab
} = useContext(TabsContext);
return (
<button
onClick={() => setActiveTab(id)}
aria-selected={activeTab === id}
>
{children}
</button>
);
}
function Panel({ id, children }) {
const { activeTab } =
useContext(TabsContext);
if (activeTab !== id) {
return null;
}
return <div>{children}</div>;
}
Usage:
function App() {
return (
<Tabs>
<div>
<Tab id="profile">Profile</Tab>
<Tab id="settings">Settings</Tab>
</div>
<Panel id="profile">
<h2>Profile</h2>
<p>Profile information</p>
</Panel>
<Panel id="settings">
<h2>Settings</h2>
<p>Account settings</p>
</Panel>
</Tabs>
);
}
The JSX clearly communicates the relationship between the Tabs, buttons, and panels.
7. What is the Difference Between Compound Components and Normal Components?
Answer:
Normal components are often used independently.
Example:
<Button>Save</Button>
A Compound Component system contains multiple related components that are designed to work together.
Example:
<Tabs>
<Tab id="profile">Profile</Tab>
<Panel id="profile">
Profile content
</Panel>
</Tabs>
The main difference is coordination.
Normal Components
Component
↓
Works independently
Compound Components
Parent
↓
Shared state/behavior
↓
Related child components
Compound Components are especially useful when several pieces of UI need to coordinate.
8. How Can a Compound Component Use a Custom Hook?
Answer:
A custom Hook can make the shared Context logic easier to reuse and provide a cleaner API.
Example:
function useTabsContext() {
const context =
useContext(TabsContext);
if (!context) {
throw new Error(
"useTabsContext must be used inside Tabs"
);
}
return context;
}
Now child components can use:
function Tab({ id, children }) {
const {
activeTab,
setActiveTab
} = useTabsContext();
return (
<button
onClick={() => setActiveTab(id)}
>
{children}
</button>
);
}
This provides an important benefit: if a component is accidentally used outside the required Provider, the custom Hook gives a clear error.
This pattern is often useful for building reusable component libraries.
9. What Happens If a Compound Component Is Used Outside Its Parent?
Answer:
If a child component depends on Context provided by its parent but is rendered outside that Provider, it may receive the Context’s default value.
For example:
<Tab id="profile">
Profile
</Tab>
If Tab expects TabsContext but there is no matching Provider, the required shared state may be unavailable.
A custom Hook can provide a clearer error:
function useTabsContext() {
const context =
useContext(TabsContext);
if (context === null) {
throw new Error(
"Tabs components must be used inside <Tabs>"
);
}
return context;
}
Now incorrect usage can be identified quickly during development.
This is especially helpful when creating reusable component systems.
10. Create a Practical Compound Component Dropdown
Answer:
A Dropdown can use Compound Components to coordinate its trigger and menu.
import {
createContext,
useContext,
useState
} from "react";
const DropdownContext =
createContext(null);
function Dropdown({ children }) {
const [open, setOpen] = useState(false);
return (
<DropdownContext.Provider
value={{
open,
setOpen
}}
>
<div>
{children}
</div>
</DropdownContext.Provider>
);
}
function Trigger({ children }) {
const {
open,
setOpen
} = useContext(DropdownContext);
return (
<button
onClick={() => setOpen(!open)}
aria-expanded={open}
>
{children}
</button>
);
}
function Menu({ children }) {
const { open } =
useContext(DropdownContext);
if (!open) {
return null;
}
return (
<div>
{children}
</div>
);
}
function Item({ children, onClick }) {
const { setOpen } =
useContext(DropdownContext);
function handleClick() {
onClick?.();
setOpen(false);
}
return (
<button onClick={handleClick}>
{children}
</button>
);
}
Usage:
function App() {
return (
<Dropdown>
<Trigger>
Options
</Trigger>
<Menu>
<Item
onClick={() =>
console.log("Profile")
}
>
Profile
</Item>
<Item
onClick={() =>
console.log("Settings")
}
>
Settings
</Item>
<Item
onClick={() =>
console.log("Logout")
}
>
Logout
</Item>
</Menu>
</Dropdown>
);
}
The structure is easy to understand:
Dropdown
├── Trigger
└── Menu
├── Item
├── Item
└── Item
The parent manages the shared open state, while the child components use that state to control their behavior.
This pattern provides a flexible API without requiring a large number of configuration props on a single component.
Key Takeaways
- Compound Components are groups of related components that work together.
- A parent component commonly manages shared state or behavior.
- Child components access shared information through props or Context.
childrenis commonly used to compose compound component structures.- Context is useful when related compound components need shared state without prop drilling.
- Tabs, Accordions, Dropdowns, Selects, and Dialogs are common use cases.
- Custom Hooks can provide a cleaner API around Context.
- A custom Hook can also validate that a child is used inside the correct parent.
- Compound Components provide flexible and readable JSX APIs.
- They can reduce the need for large components with many configuration props.
- Each compound component should have a clear responsibility.
- Compound Components are a design pattern, not a separate React feature.
FAQs
1. What are Compound Components in React?
Compound Components are a group of related components that work together as a single reusable UI pattern, usually sharing state or behavior.
2. What is a common example of Compound Components?
Tabs, Accordions, Dropdowns, Select menus, Dialogs, and similar interactive UI systems are common examples.
3. Do Compound Components require Context API?
No. Context is commonly useful for sharing state between related components, but Compound Components can also be implemented using other techniques such as props and React elements.
4. Why is Context useful in Compound Components?
Context allows related child components to access shared state and functions without requiring those values to be passed through multiple intermediate components.
5. Can Compound Components use Custom Hooks?
Yes. Custom Hooks can encapsulate Context access and shared logic, making compound component implementations cleaner and easier to maintain.
6. What is the difference between Component Composition and Compound Components?
Component Composition is the broader practice of combining components. Compound Components are a specific composition pattern where related components coordinate to provide a unified UI behavior.
7. Are Compound Components suitable for every React component?
No. They are most useful when several related UI pieces need to work together. For simple components, regular props and children may be enough.
Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.
