Compound Components
Learn compound components pattern for creating flexible and reusable component APIs
75 min•By Priygop Team•Last updated: Feb 2026
What are Compound Components?
Compound components are a pattern where components are used together such that they share an implicit state that lets them communicate with each other. This pattern provides a flexible and intuitive API for complex components.
Basic Compound Components
Example
// Basic compound components example
import React, { createContext, useContext, useState } from 'react';
const TabsContext = createContext();
function Tabs({ children, defaultIndex = 0 }) {
const [activeIndex, setActiveIndex] = useState(defaultIndex);
return (
<TabsContext.Provider value={{ activeIndex, setActiveIndex }}>
<div className="tabs">
{children}
</div>
</TabsContext.Provider>
);
}
function TabList({ children }) {
return (
<div className="tab-list">
{children}
</div>
);
}
function Tab({ index, children }) {
const { activeIndex, setActiveIndex } = useContext(TabsContext);
const isActive = index === activeIndex;
return (
<button
className={`tab ${isActive ? 'active' : ''}`}
onClick={() => setActiveIndex(index)}
>
{children}
</button>
);
}
function TabPanels({ children }) {
return (
<div className="tab-panels">
{children}
</div>
);
}
function TabPanel({ index, children }) {
const { activeIndex } = useContext(TabsContext);
if (index !== activeIndex) return null;
return (
<div className="tab-panel">
{children}
</div>
);
}
// Usage
function App() {
return (
<Tabs defaultIndex={0}>
<TabList>
<Tab index={0}>Home</Tab>
<Tab index={1}>About</Tab>
<Tab index={2}>Contact</Tab>
</TabList>
<TabPanels>
<TabPanel index={0}>
<h2>Home Content</h2>
<p>Welcome to our website!</p>
</TabPanel>
<TabPanel index={1}>
<h2>About Content</h2>
<p>Learn more about us.</p>
</TabPanel>
<TabPanel index={2}>
<h2>Contact Content</h2>
<p>Get in touch with us.</p>
</TabPanel>
</TabPanels>
</Tabs>
);
}Advanced Compound Components
Example
// Advanced compound components with flexible API
import React, { createContext, useContext, useState, cloneElement, Children } from 'react';
const AccordionContext = createContext();
function Accordion({ children, multiple = false }) {
const [openItems, setOpenItems] = useState(new Set());
const toggleItem = (index) => {
if (multiple) {
const newOpenItems = new Set(openItems);
if (newOpenItems.has(index)) {
newOpenItems.delete(index);
} else {
newOpenItems.add(index);
}
setOpenItems(newOpenItems);
} else {
setOpenItems(new Set([openItems.has(index) ? null : index]));
}
};
const isOpen = (index) => openItems.has(index);
return (
<AccordionContext.Provider value={{ toggleItem, isOpen }}>
<div className="accordion">
{Children.map(children, (child, index) =>
cloneElement(child, { index })
)}
</div>
</AccordionContext.Provider>
);
}
function AccordionItem({ index, children }) {
const { toggleItem, isOpen } = useContext(AccordionContext);
const open = isOpen(index);
return (
<div className={`accordion-item ${open ? 'open' : ''}`}>
{Children.map(children, child =>
cloneElement(child, { index, open, toggleItem })
)}
</div>
);
}
function AccordionTrigger({ index, children, open, toggleItem }) {
return (
<button
className="accordion-trigger"
onClick={() => toggleItem(index)}
>
{children}
<span className={`arrow ${open ? 'up' : 'down'}`}>▼</span>
</button>
);
}
function AccordionContent({ open, children }) {
return (
<div className={`accordion-content ${open ? 'open' : ''}`}>
{children}
</div>
);
}
// Usage
function App() {
return (
<Accordion multiple>
<AccordionItem>
<AccordionTrigger>Section 1</AccordionTrigger>
<AccordionContent>
<p>Content for section 1</p>
</AccordionContent>
</AccordionItem>
<AccordionItem>
<AccordionTrigger>Section 2</AccordionTrigger>
<AccordionContent>
<p>Content for section 2</p>
</AccordionContent>
</AccordionItem>
</Accordion>
);
}