Search justacoding.blog. [Enter] to search. Click anywhere to close.

November 7th, 2021

A Simple React Accordion Component Example

This article will walk through some of the key points and aspects of an accordion build within React. You can find the accompanying JSFiddle component over here: React accordion component example.

This is a small and relatively simple component.

But that’s why it’s a good example for React beginners to look at. Alongside the accompanying accordion component example provided, this article will serve as something of a React accordion tutorial.

With that in mind, let’s take a look at how the accordion works!

What this React accordion component actually does

Accordions are a satisfying way to display information.

The user can expand out the items within the accordion to display the information within. The accordion can be composed of many different items, each with varying lengths (and types) of content nested inside.

This React accordion component example in particular has the follow functionalities:

  • Define the items that should comprise the accordion by passing an array of config objects
  • Expand out items by clicking the title bar
  • Optionally, modify the behaviour with regards to how many items can be active (or expanded) at once and whether items other than the selected item should automatically minimize
  • Optionally, supply header and/or footer elements to wrap the accordion with

So it’s essentially a standard accordion, but with a few other handy pieces of functionality included.

Let’s take a look at the general structure of the component.

The general structure of the accordion component

In simple terms, this React accordion component works by receiving an array of items. These items are what make up the accordion itself, each item contains it’s own information or content.

Technically, the items are represented initially by regular JavaScript objects. Each item has a title property, a content property and an optional subtitle property:

// example of a config object passed to the Accordion component
// the component expects an array of items like this one

{ 
  title: "Cycads", 
  subtitle: "Exotic plants",
  content: <p>Cycads have a cylindrical trunk which usually...</p>
}

The title and subtitle properties are hopefully self-explanatory. These describe what is listed in the header/title area for each individual accordion item element.

Likewise for the content property. This simply represents the content that is nested within the given accordion item — visible once the item is expanded open, and hidden once it’s closed or minimized.

This content can be any React element, so we can pass in other components and so on too.

Once we’ve determined how our items should look and how they should be configured, we can use these config items to start building our JSX output.

Mapping the items in the main accordion component

Given an array of items passed in as props, we can go about mapping these items and outputting them within the main accordion component itself.

I’ve decided to use an AccordionItem component for this:

// the AccordionItem presentational component

const AccordionItem = ({ children, title, subtitle, isActive, onItemClick }) => {
  return (
    <div className={`accordionItem ${isActive ? "active" : ""}`}>
      <div className="accordionTitle" onClick={onItemClick}>
        <h3>
          {title}
          <span>{isActive ? "-" : "+"}</span>
        </h3>
        {!!subtitle && <p className="accordionSubtitle">{subtitle}</p>}
      </div>
      <div className="accordionContent">
        {children}
      </div>
    </div>
  )
}

This is a basic presentational component that is responsible for actually displaying the items within the accordion. Each item defined in the items prop will be represented by one of these AccordionItems within the JSX output.

Whether each item is active (visible, or expanded) or not is determined by the parent component, Accordion. This is to keep the presentational component as simple and as straightforward as possible, and to ensure it’s concerned with the look and feel of the content that it is representing.

(I touch briefly on presentational components, and their usages, in my other article: React Tips For Beginners – How To Structure Components (3 Tips))

Let’s look at how we can go about expanding and minimizing the items in our React accordion component via our Accordion.

Expanding and minimizing accordion items

For this, we only need one useState hook:

const [activeItemIndexes, setActiveItemIndexes] = 
  useState([initialActiveItemIndex || 0])

I’m using an array of active items here, as opposed to a single active item, because in my implementation it’s possible for more than one item to be expanded or open at one time.

Activating items is as simple as maintaining this array upon user interaction.

Zero-indexed accordion items

You’ll notice that we are referring to the index of the item in order to identify it in each case.

This is the easiest and most efficient approach, and is perfectly sufficient for what we need.

So, to determine if an accordion item should be active or not — it’s simply a matter of consulting with this array of indexes (activeItemIndexes). The result (a boolean) is then passed down to the AccordionItem component, whereby the necessary logic can either apply the active class or not.

The active class contains CSS styling to ensure that the accordion item is “expanded”. By default the accordion item content blocks are hidden or minimized.

An option to allow multiple items to be active or not

I decided to have an optional prop on Accordion that can be used to determine if multiple accordion items can be active or not — closeOtherItemsOnClick.

The default behaviour is for only one accordion item to be active or expanded at one time.

With this prop passed in, however, all items can be activated if required.

When selecting or clicking the necessary element in the UI (so in this case, the title bar in an AccordionItem) this logic should be accounted for before making any state updates:

// the function responsible for expanding and minimizing accordion items
// this function lives in the Accordion component

const handleItemClick = (index) => {
  if (closeOtherItemsOnClick) {
    setActiveItemIndexes(activeItemIndexes.includes(index) ? [] : [index])
    return 
  }
  
  let newActiveItemIndexes = [...activeItemIndexes]
  if (newActiveItemIndexes.includes(index)) {
    newActiveItemIndexes = newActiveItemIndexes.filter(item => item !== index)
  } else {
    newActiveItemIndexes.push(index)
  }
  setActiveItemIndexes(newActiveItemIndexes)
}

So we’ll either need to maintain a single item in state (and close/deactivate all other items) or append the item to the already activated items. This depends on what mode we are working in, and that’s based on the supplied prop (closeOtherItemsOnClick).

Of course, clicking an already active accordion item should close the item down. By this, we are saying that the accordion items should be “toggleable”. This behaviour needs to be accounted for too, as demonstrated in the snippet above.

The function above handles all of the interactions within the accordion. We use this any time we need to interact with the activeItemIndexes array in state.

This way, we can be sure we are modifying the state correctly in each case based on the various configurations in place.

Preselecting a specific item

You’ll notice that the main Accordion component accepts a prop to preselect a specific item. This item will be the first visible item once the user first opens or loads the accordion:

initialActiveItemIndex={3} 

By default, the very first item is the active one.

Passing this optional prop, initialActiveItemIndex, however, allows the user to override this default behaviour.

It’s simply a means of specifying which item should be active initially. This is achieved by passing the index of the item; so effectively it’s position amongst the other items (zero-indexed, of course).

Styling the accordion component

This React accordion example contains some basic styling.

Alongside this, I’ve added some “active” styles and so on to help with the usability and the UX in general.

The main examples of this are:

  • The active accordion items have darker (black) title text than the inactive accordion items
  • The active items have a - alongside their titles — indicating that these items can be closed
  • The inactive items have a + alongside their titles, this time indicating that they can be opened, instead

This type of UX helps the user easily understand that the accordion items are toggleable elements and can be opened and closed as such.

In general it’s always best to consider these kind of contextual clues or hints for the user, it just makes the design more user-friendly.

Responsiveness

If you resize your browser, you’ll see that this React accordion component is fairly responsive by default.

That’s primarily because it’s a full-width element, and as such, it looks fine on a mobile as well as a desktop view. We don’t need to consider how to stack elements vertically as the screen resizes or shrinks, because the elements are already stacked (vertically) to begin with.

In reality, you wouldn’t necessarily want a full-width accordion on desktop, it’d be far too big. But you can easily nest the accordion component within a parent container, and supply a max-width here.

Or perhaps you could pass another prop to control the various dimensions of the accordion.

Animating the accordion component

You’ll notice that this React accordion is animated. When expanding out items, the items “scroll” downwards.

It’s easy to overdo animations, particularly when first starting out with web development.

However, there is a time and a place for animations. The priority should always be to create a functional UX.

Well placed animations can assist with this.

In the React accordion example we have here, animating the items on selection helps draw the user’s eye to the relevant element in the screen. This is helpful when layout elements “shift” (as they do in accordions).

The animation is nice from an aesthetic standpoint; but it also assists somewhat with usability too.

Using transition with transform

The animations achieved here use transition alongside transform.

.accordion .accordionItem .accordionContent {
  padding: 0 16px;
  overflow: hidden;
  transition: all .1s ease-in-out;
  transform: scaleY(0);
  height: 0;
}

This means that the element will ease in and out during it’s relevant interactions.

Transforming the scale means we can effectively entirely shrink the element vertically — so it’s hidden. And then scale it back up (to 1), vertically, so it’s full height once more:

.accordion .accordionItem.active .accordionContent {
  transform: scaleY(1);
  height: auto;
  padding: 16px;
}

That’s the basis of how these simple animations work.

Supplying a custom header and a custom footer

This React accordion example also accepts props to set the header and footer elements of the accordion.

These elements are simply placed above and below the main accordion, respectively, and they are a handy way to display more relevant information as appropriate. In my case, I’m passing in these two simple components as header and footer items respectively:

const CustomHeader = () => {
  return <h1 className="customHeader">Types of plant</h1>
}

const CustomFooter = () => {
  return <div className="customFooter">For more info, visit our main website</div>
}

But you can pass in whatever items you require. These items aren’t considered part of the “core” accordion component itself.

They are optional props, and these elements can easily be omitted if not required.

Adapting this accordion component to create a tabs component

The behaviour implemented in this React accordion component example is relatively straightforward.

We’re basically just activating and deactivating different parts or sections of the design; showing them and hiding them as required.

Given this, we can basically re-use the component to make a tabs component, so something like this:

tabs component
The rough layout for a tabs component, different tabs can be toggled much like with the accordion component example discussed

Tabbed mode wouldn’t support having multiple active items at once, so we’d need to make an adjustment there. Only one tab (or item, to use consistent terminology) should ever be active at once in this mode — or that would lead to some very confusing UX!

Of course, the layout would need adjusting slightly too, in tabbed mode.

The actual tab headers run horizontally across the screen, and the content areas sit beneath that. So our component would need to reflect this layout when in tabbed mode.

So with these few simple adjustments – we could repurpose the accordion component fairly easily to have it function as a tabbed component, instead of an accordion.

In summary

I hope you’ve gained something from this article and the accompanying React accordion example!

As you can see, this accordion component is fairly compact. It’s basic enough — but it still does what we require from it.

If you’re interested in more React code examples like this one, please do feel free to check out my React Calendar Component Example (With Events) implementation.

This component is slightly bigger with a few more moving parts, it’s still a good one for beginners to sink their teeth into.

The code examples category in general will also be populated with more small projects like these ones over time.

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog