Design Pattern in React Part 2:- Building Flexible and Reusable Components with the Compound Pattern in React

Sunny Yadav
3 min readJun 8, 2024

What is the Compound Component Pattern?

  • The Compound Component Pattern allows us to create multiple components that collaborate to perform a single task.
  • Instead of exposing all functionality through a single component, we split it into smaller, specialized components.
  • These components work together to achieve a cohesive behavior.

How Does It Work?

  • We define a parent component that wraps the compound components.
  • Each compound component is a child of the parent and contributes to the overall functionality.
  • The parent component manages the shared state or logic, while the compound components focus on presentation and behavior.

Key Concepts

  • Parent Component: Manages the state and behavior, providing the necessary context and functions to the child components.
  • Child Components: Consume the context provided by the parent component and handle the rendering. They are designed to work together within the parent component’s context.

Benefits

  • Reusability: Child components can be reused in different parts of your application.
  • Modularity: Each component has a single responsibility, making it easier to manage and maintain.
  • Flexibility: You can change the composition of child components without altering their internal logic.

Example

Let’s consider an example of a compound component: a Tabs component.

  1. Create the Context -

First, we create a context to manage the state and behavior.

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

const TabsContext = createContext();

const Tabs = ({ children, defaultIndex = 0 }) => {
const [selectedIndex, setSelectedIndex] = useState(defaultIndex);

return (
<TabsContext.Provider value={{ selectedIndex, setSelectedIndex }}>
{children}
</TabsContext.Provider>
);
};

const useTabsContext = () => {
const context = useContext(TabsContext);
if (!context) {
throw new Error('useTabsContext must be used within a Tabs provider');
}
return context;
};

2. Create Child Components -
Next, we create the child components: TabList, Tab, and TabPanels.

const TabList = ({ children }) => {
return <div className="tab-list">{children}</div>;
};

const Tab = ({ index, children }) => {
const { selectedIndex, setSelectedIndex } = useTabsContext();

return (
<button
className={`tab ${selectedIndex === index ? 'active' : ''}`}
onClick={() => setSelectedIndex(index)}
>
{children}
</button>
);
};

const TabPanels = ({ children }) => {
const { selectedIndex } = useTabsContext();

return <div className="tab-panels">{children[selectedIndex]}</div>;
};

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

3. Use the Compound Component

Now we can use these components together in the Tabs component to create a complete tabbed interface.

const App = () => {
return (
<Tabs defaultIndex={0}>
<TabList>
<Tab index={0}>Tab 1</Tab>
<Tab index={1}>Tab 2</Tab>
<Tab index={2}>Tab 3</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
<TabPanel>Content 3</TabPanel>
</TabPanels>
</Tabs>
);
};

export default App;

Explanation

  • Tabs Component: It manages the state of which tab is selected and provides this state to its children through context.
  • TabList Component: It is a wrapper for the individual Tab components.
  • Tab Component: It receives the index prop to identify itself and uses the context to know if it is the currently selected tab.
  • TabPanels Component: It wraps the content of each tab and displays the content corresponding to the currently selected tab.
  • TabPanel Component: It contains the content for each individual tab.

Conclusion

The compound pattern is a versatile and powerful approach in React for building complex components from smaller, focused ones. It encourages reusability, modularity, and flexibility, making your codebase easier to manage and extend. By using context and composition, you can create highly dynamic and interactive UI components that are maintainable and scalable.

--

--

Sunny Yadav

Frontend engineer. Loves clean code & user-friendly design. Proficient in HTML, CSS, JS, TS, React.JS and Next.JS. Constantly learning