Building flexible and expressive React components
Tags: react, webdev, javascript
Published: 2021-03-10 13:59:16

---
title: Building flexible and expressive React components
published: true
description: How to use the compound component pattern to build flexible and expressive React components
tags: react,webdev,javascript
canonical_url: https://antongunnarsson.com/compound-components-in-react/
---

Sometimes you find yourself in a situation where you need to build a component that is reusable but also very flexible so it can adapt to a myriad of situations. It could be something like a toggle, tabbed navigation, or an accordion. A pattern that I think is fantastic for solving these problems is **compound components**. That is when you have multiple components working together in a coupled but flexible way.

Let's look at how we could build this accordion as a compound component:

![accordion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6zw147qwlhwpjaaiphe8.gif)
 
> You can find an interactive version of this article with some extracurricular information [over here](https://antongunnarsson.com/compound-components-in-react/).

This accordion consists of three components: One for the toggling button, one for the collapsible content, and one parent that ties everything together. Here's how it looks when using it:

```jsx
<Accordion>
  <div>
    <Accordion.Toggle eventKey="item1">Item 1</Accordion.Toggle>
    <Accordion.Content eventKey="item1">Content 1...</Accordion.Content>
  </div>
  <div>
    <Accordion.Toggle eventKey="item2">Item 2</Accordion.Toggle>
    <Accordion.Content eventKey="item2">Content 2...</Accordion.Content>
  </div>
  <div>
    <Accordion.Toggle eventKey="item3">Item 3</Accordion.Toggle>
    <Accordion.Content eventKey="item3">Content 3...</Accordion.Content>
  </div>
</Accordion>
```

There's a couple of things to note here. First off `<Accordion.Toggle>` and `<Accordion.Content>` might look a little weird with it if you haven't seen it before, but it is because the `<Toggle>` and `<Content>` components are added as static properties on `<Accordion>`. This is not a requirement in any way but I think it makes it clearer that we are dealing with tightly coupled components intended to be used together.

> If you've ever used [Reach UI](https://reach.tech/) or [React Bootstrap](https://react-bootstrap.github.io/) this probably looks familiar to you. They use this pattern for accordions and other components.

Secondly neither the `<Toggle>` and `<Content>` components are direct children of `<Accordion>`. This is possible because we use a Context to pass data between our components, which gives us a lot of flexibility in how we want our accordion to look and work.

Here is how the parent component `<Accordion>` is implemented:

```jsx
const AccordionContext = React.createContext()

function Accordion({ children }) {
  const [activeKey, setActiveKey] = useState(null)

  const setKey = (newKey) => {
    if (newKey === activeKey) setActiveKey(null)
    else setActiveKey(newKey)
  }

  return <AccordionContext.Provider value={{ activeKey, setKey }}>{children}</AccordionContext.Provider>
}
```

There's a bit to unpack here so let's start at the top. We create a context called `AccordionContext` that will be used to connect our components. Then we set up some state, `activeKey`, to hold the currently open "tab"s key and create a function that updates our state to open/close the accordion. Lastly, we return the component's `children` wrapped in our contexts `<Provider>` which value we set to an object with `activeKey` and `setKey`.

With that set up we can take a look at `<Content>` where the implementation looks like this:

```jsx
function Content({ children, eventKey }) {
  const { activeKey } = useContext(AccordionContext)

  if (activeKey !== eventKey) return null

  return children
}
```

We get `activeKey` from the `AccordionContext` and if it doesn't match the prop `eventKey` we return `null`, otherwise we return `children`.

Finally, this is how `<Toggle>` looks:

```jsx
function Toggle({ children, eventKey }) {
  const { setKey } = useContext(AccordionContext)

  return <button onClick={() => setKey(eventKey)}>{children}</button>
}
```

Here we grab `setKey` from our `AccordionContext` and when the user clicks the button we call it with the toggles `eventKey` as its only argument.

And with that our accordion is complete! You can find the complete implementation [here](https://github.com/anton-g/blog/blob/master/content/blog/compound-components-in-react/components/Accordion.js).

So to recap: By splitting the implementation across multiple components that share an implicit state using `Context` we've created a more expressive and flexible API for our accordion. If we suddenly decide we want to have the accordion buttons below the content we only need to change the usage and not the implementation. This is one of the biggest benefits of compound components.

Hopefully this has given you some insight into why and when compound components are useful. I think it should be a tool in every React developers toolbox.

You're more than welcome to hit me up on [Twitter](https://twitter.com/awnton) if you have any suggestions or feedback!

Thanks for reading!