Compound Components
0/3 in this phase0/36 across the roadmap
📖 Concept
Compound Components is a pattern where several components work together to maintain a shared implicit state. Think of the HTML <select> and <option> elements. You don't pass an array of options to the select; instead, you nest the options inside.
Benefits:
- Reduced Prop Drilling: You don't need to pass many props to a single "god" component.
- Flexibility: The consumer (developer using your component) has control over the order and placement of sub-components.
- Clean API: The usage looks more like standard HTML.
Implementation: Typically implemented using the Context API to share state between the parent and its children without passing props explicitly.
💻 Code Example
code
1import React, { useState, useContext, createContext } from 'react';23// 1. Create a Context for the compound component4const AccordionContext = createContext();56// 2. Parent Component (Provider)7function Accordion({ children }) {8 const [openIndex, setOpenIndex] = useState(null);910 const toggle = (index) => {11 setOpenIndex(openIndex === index ? null : index);12 };1314 return (15 <AccordionContext.Provider value={{ openIndex, toggle }}>16 <div className="border rounded-md divide-y overflow-hidden shadow-sm">17 {children}18 </div>19 </AccordionContext.Provider>20 );21}2223// 3. Child Components (Consumers)24function AccordionItem({ index, children }) {25 return (26 <div className="bg-white">27 {React.Children.map(children, (child) =>28 React.cloneElement(child, { index })29 )}30 </div>31 );32}3334function AccordionHeader({ index, children }) {35 const { openIndex, toggle } = useContext(AccordionContext);36 const isOpen = openIndex === index;3738 return (39 <button40 className="w-full px-4 py-3 text-left font-medium flex justify-between items-center hover:bg-gray-50 transition"41 onClick={() => toggle(index)}42 >43 {children}44 <span>{isOpen ? '−' : '+'}</span>45 </button>46 );47}4849function AccordionPanel({ index, children }) {50 const { openIndex } = useContext(AccordionContext);51 if (openIndex !== index) return null;5253 return (54 <div className="px-4 py-3 bg-gray-50 text-gray-700 animate-in fade-in duration-200">55 {children}56 </div>57 );58}5960// 4. Attach sub-components to the parent for easier access (optional)61Accordion.Item = AccordionItem;62Accordion.Header = AccordionHeader;63Accordion.Panel = AccordionPanel;6465// 5. Usage:66function App() {67 return (68 <Accordion>69 <Accordion.Item index={0}>70 <Accordion.Header>What is React?</Accordion.Header>71 <Accordion.Panel>React is a JavaScript library for building UIs.</Accordion.Panel>72 </Accordion.Item>73 <Accordion.Item index={1}>74 <Accordion.Header>Why use Compound Components?</Accordion.Header>75 <Accordion.Panel>Because they provide a clean and flexible API!</Accordion.Panel>76 </Accordion.Item>77 </Accordion>78 );79}8081export default App;
🏋️ Practice Exercise
- Build a 'Tabs' component using the Compound Components pattern (Tabs, TabList, Tab, TabPanel).
- Create a 'Modal' component with 'Modal.Trigger', 'Modal.Window', and 'Modal.Close'.
- Implement a 'MultiStepForm' where the state of the current step is shared implicitly.
- Try to implement the pattern without using
React.Children.map(use Context instead). Discuss the pros and cons.
⚠️ Common Mistakes
Not providing a fallback for the Context, leading to errors if children are used outside the parent.
Over-using the pattern for simple components that don't need it.
Breaking the implicit contract by nesting components too deeply (Context solves this!).
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Compound Components
Was this topic helpful?