React Course
React
/
Advanced

Compound Components Pattern

Definition

A design pattern where multiple components work together to share state and logic implicitly, allowing the developer to declaratively assemble the UI without passing dozens of props.

Explain Like I'm New

Think of the standard HTML `<select>` and `<option>` tags. They are separate tags, but they magically communicate. When you click an `<option>`, the `<select>` knows about it. The Compound Components pattern allows you to build your own React components that work exactly like this.

Real World Example

Building a `<Tabs>` component. Instead of passing an array of objects to `<Tabs data={myTabs} />`, you use Compound Components: `<Tabs><TabList><Tab>Profile</Tab></TabList><TabPanels><Panel>Profile Data</Panel></TabPanels></Tabs>`.

Common Use Cases

  • Building highly flexible UI libraries (like Headless UI or Radix)
  • Avoiding 'Prop explosion' (components that take 20 different props to configure layout)

Interactive Example

import React, { useState, createContext, useContext } from 'react';

// 1. Create a Context for the compound components to share
const ToggleContext = createContext();

// 2. The Parent Component manages the state
export function Toggle({ children }) {
  const [on, setOn] = useState(false);
  return (
    <ToggleContext.Provider value={{ on, setOn }}>
      <div className='p-4 border rounded bg-gray-50'>{children}</div>
    </ToggleContext.Provider>
  );
}

// 3. Child components consume the context
export function ToggleOn({ children }) {
  const { on } = useContext(ToggleContext);
  return on ? children : null;
}

export function ToggleOff({ children }) {
  const { on } = useContext(ToggleContext);
  return on ? null : children;
}

export function ToggleButton() {
  const { on, setOn } = useContext(ToggleContext);
  return <button onClick={() => setOn(!on)} className='bg-blue-500 text-white p-2 rounded'>Toggle</button>;
}

// 4. Usage: Highly declarative and flexible!
export default function App() {
  return (
    <Toggle>
      <ToggleButton />
      <hr className='my-2'/>
      <ToggleOn>The button is ON!</ToggleOn>
      <ToggleOff>The button is OFF!</ToggleOff>
    </Toggle>
  );
}

Interview Questions

basic

  • What is a Compound Component?
  • Why not just pass a big configuration object as a prop?

intermediate

  • How do the parent and child components communicate in this pattern?
  • How do you restrict children to only be specific components?

advanced

  • What is the difference between using `React.Children.map` and using Context for this pattern?
  • How does this pattern improve flexibility?

Flash Cards

Question

Why not just pass a big config object?

Click to reveal answer
Answer

If you pass a config object, the layout is rigid. If a developer wants to put an icon next to the tab text, or change the order of the panels, they can't unless you add MORE props. Compound components let the developer write the layout themselves using standard JSX.

Question

How do they communicate?

Click to reveal answer
Answer

Historically, React used `React.cloneElement` to inject props into children. In modern React, the parent component creates a React Context, and the child components `useContext` to read and update the shared state implicitly.