Skip to main content
Course/Module 10/Topic 3 of 4Advanced

Advanced Compound Components

Master compound components pattern to create flexible and composable component APIs that work together seamlessly.

90 minBy Priygop TeamLast updated: Feb 2026

Understanding Compound Components

Compound components are a pattern where multiple components work together to form a complete UI. They share implicit state and provide a more flexible API than a single monolithic component.

Basic Compound Components

Example
// Basic compound component example
const Tabs = ({ children, defaultTab }) => {
    const [activeTab, setActiveTab] = useState(defaultTab);
    
    return (
        <div className="tabs">
            {React.Children.map(children, child => 
                React.cloneElement(child, { activeTab, setActiveTab })
            )}
        </div>
    );
};

const TabList = ({ children, activeTab, setActiveTab }) => (
    <div className="tab-list">
        {React.Children.map(children, (child, index) =>
            React.cloneElement(child, {
                isActive: activeTab === index,
                onClick: () => setActiveTab(index)
            })
        )}
    </div>
);

const Tab = ({ children, isActive, onClick }) => (
    <button 
        className={`tab ${isActive ? 'active' : ''}`}
        onClick={onClick}
    >
        {children}
    </button>
);

const TabPanels = ({ children, activeTab }) => (
    <div className="tab-panels">
        {children[activeTab]}
    </div>
);

const TabPanel = ({ children }) => (
    <div className="tab-panel">
        {children}
    </div>
);

// Usage
<Tabs defaultTab={0}>
    <TabList>
        <Tab>Tab 1</Tab>
        <Tab>Tab 2</Tab>
    </TabList>
    <TabPanels>
        <TabPanel>Content 1</TabPanel>
        <TabPanel>Content 2</TabPanel>
    </TabPanels>
</Tabs>

Advanced Compound Patterns

Example
// Using Context for compound components
const SelectContext = createContext();

const Select = ({ children, value, onChange }) => {
    const [isOpen, setIsOpen] = useState(false);
    
    const contextValue = {
        value,
        onChange,
        isOpen,
        setIsOpen
    };
    
    return (
        <SelectContext.Provider value={contextValue}>
            <div className="select">
                {children}
            </div>
        </SelectContext.Provider>
    );
};

const SelectTrigger = ({ children }) => {
    const { isOpen, setIsOpen } = useContext(SelectContext);
    
    return (
        <button 
            className="select-trigger"
            onClick={() => setIsOpen(!isOpen)}
        >
            {children}
        </button>
    );
};

const SelectContent = ({ children }) => {
    const { isOpen } = useContext(SelectContext);
    
    if (!isOpen) return null;
    
    return (
        <div className="select-content">
            {children}
        </div>
    );
};

const SelectItem = ({ value, children }) => {
    const { value: selectedValue, onChange, setIsOpen } = useContext(SelectContext);
    
    const handleClick = () => {
        onChange(value);
        setIsOpen(false);
    };
    
    return (
        <div 
            className={`select-item ${selectedValue === value ? 'selected' : ''}`}
            onClick={handleClick}
        >
            {children}
        </div>
    );
};

Compound Component Best Practices

  • Use Context for complex state sharing
  • Provide sensible defaults for all components
  • Allow flexible composition order
  • Use displayName for better debugging
  • Handle edge cases gracefully
  • Document the component API clearly

Common Compound Patterns

Example
// Accordion compound component
const Accordion = ({ children, allowMultiple = false }) => {
    const [openItems, setOpenItems] = useState(new Set());
    
    const toggleItem = (itemId) => {
        setOpenItems(prev => {
            const newSet = new Set(prev);
            if (newSet.has(itemId)) {
                newSet.delete(itemId);
            } else {
                if (!allowMultiple) {
                    newSet.clear();
                }
                newSet.add(itemId);
            }
            return newSet;
        });
    };
    
    return (
        <AccordionContext.Provider value={{ openItems, toggleItem }}>
            {children}
        </AccordionContext.Provider>
    );
};

const AccordionItem = ({ children, itemId }) => (
    <div className="accordion-item">
        {React.Children.map(children, child =>
            React.cloneElement(child, { itemId })
        )}
    </div>
);

const AccordionTrigger = ({ children, itemId }) => {
    const { openItems, toggleItem } = useContext(AccordionContext);
    const isOpen = openItems.has(itemId);
    
    return (
        <button 
            className={`accordion-trigger ${isOpen ? 'open' : ''}`}
            onClick={() => toggleItem(itemId)}
        >
            {children}
        </button>
    );
};

Mini-Project: Form Compound Component

Example
// Complete form compound component system
import React, { createContext, useContext, useState } from 'react';

const FormContext = createContext();

const Form = ({ children, onSubmit, initialValues = {} }) => {
    const [values, setValues] = useState(initialValues);
    const [errors, setErrors] = useState({});
    const [touched, setTouched] = useState({});
    
    const setValue = (name, value) => {
        setValues(prev => ({ ...prev, [name]: value }));
        if (errors[name]) {
            setErrors(prev => ({ ...prev, [name]: '' }));
        }
    };
    
    const setError = (name, error) => {
        setErrors(prev => ({ ...prev, [name]: error }));
    };
    
    const setTouchedField = (name) => {
        setTouched(prev => ({ ...prev, [name]: true }));
    };
    
    const handleSubmit = (e) => {
        e.preventDefault();
        onSubmit(values);
    };
    
    const contextValue = {
        values,
        errors,
        touched,
        setValue,
        setError,
        setTouchedField
    };
    
    return (
        <FormContext.Provider value={contextValue}>
            <form onSubmit={handleSubmit}>
                {children}
            </form>
        </FormContext.Provider>
    );
};

const FormField = ({ name, children }) => {
    const { values, errors, touched, setValue, setTouchedField } = useContext(FormContext);
    
    const value = values[name] || '';
    const error = errors[name];
    const isTouched = touched[name];
    
    return (
        <div className="form-field">
            {React.Children.map(children, child =>
                React.cloneElement(child, {
                    name,
                    value,
                    error,
                    isTouched,
                    onChange: (e) => setValue(name, e.target.value),
                    onBlur: () => setTouchedField(name)
                })
            )}
        </div>
    );
};

const FormInput = ({ name, value, error, isTouched, onChange, onBlur, ...props }) => (
    <div>
        <input
            {...props}
            name={name}
            value={value}
            onChange={onChange}
            onBlur={onBlur}
            className={`form-input ${error && isTouched ? 'error' : ''}`}
        />
        {error && isTouched && (
            <span className="form-error">{error}</span>
        )}
    </div>
);

const FormLabel = ({ children, ...props }) => (
    <label className="form-label" {...props}>
        {children}
    </label>
);

const FormButton = ({ children, type = "submit", ...props }) => (
    <button type={type} className="form-button" {...props}>
        {children}
    </button>
);

// Usage
<Form onSubmit={(values) => console.log(values)}>
    <FormField name="email">
        <FormLabel>Email</FormLabel>
        <FormInput type="email" placeholder="Enter your email" />
    </FormField>
    
    <FormField name="password">
        <FormLabel>Password</FormLabel>
        <FormInput type="password" placeholder="Enter your password" />
    </FormField>
    
    <FormButton>Submit</FormButton>
</Form>

Additional Resources

Recommended Reading

  • React Advanced Patterns Guide
  • Higher-Order Components Documentation
  • Render Props Pattern Guide
  • Compound Components Best Practices

Online Resources

  • Advanced React Patterns Tutorial
  • React Component Composition
  • Reusable Component Design
  • React Performance Patterns
Chat on WhatsApp
Priygop - Leading Professional Development Platform | Expert Courses & Interview Prep