Create accordion in react

Learn how to create an accordion in react.

An accordion is an collapsible component in which panel can be shown or hidden by clicking on it.

We will also create the same component with some extra options so that it can be extended and reused in different cases.

To fasten our development we will utilize some extra packages.

  • prop-types: To validate the props that we will receive in our component.
  • classnames: This helps us to use CSS classes as javascript objects, to make it work we just need to name our CSS file as filename.module.css. We can also dynamically add classes using this.

Following is the folder structure of our component.
React Accordion Folder Structure

Now as we are ready with our setup, lets start creating our component.

First, import all the required packages at the top.

import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";

const Accordion = (props) => {
   //other code will go here
}

export default Accordion.

As we don’t want our component to maintain any state because it should be controlled by the parent, that is why we are creating a functional component.

Also in the parameter itself we can de-structure our props object which is much better in maintaining.

const Accordion = ({ children, onChange, isOpen, label }) => {}

Validate the props so that we make sure we get the right things. This validation should be done out side our function by setting the object property.

Accordion.propTypes = {
  isOpen: PropTypes.bool.isRequired,
  onChange: PropTypes.func,
  children: PropTypes.node,
  label: PropTypes.string.isRequired
};

Accordion.defaultProps = {
  isOpen: false,
  children: null,
  label: "Accordion"
};
  • isOpen:- To check the state of the component.
  • label:- Which should be displayed on the header or the toggle button.
  • children:- Content of the panel area.
  • onChange:- Callback function to return the changes to the parent.

As we have right props with us now, let create the structure of component.

Our component will be composed of three parts.
1. A button which toggle the accordion.
2. A panel or display area which show or hide based on its state.
3. A wrapper to hold the above two together.

return (
    <div className={styles.wrapper}>
      <span
        className={cx(styles.toggler, { [styles.active]: isOpen })}
        onClick={onChangeHandler}
      >
        {label}
      </span>
      <div
        className={cx(styles.panel, {
          [styles.active]: isOpen
        })}
      >
        <div className={styles.contentWrapper}>{children}</div>
      </div>
    </div>
  );

{cx(styles.panel, { [styles.active]: isOpen })} is the dynamic method to add the style using classnames. In this styles.panel will always be there but the [styles.active] will be added only when isOpen is true.

After this the last thing pending is listening to the click event and returning the data to the parent. So lets do it.

const onChangeHandler = () => {
  onChange && onChange(!isOpen);
};

Complete code of accordion component in react.

import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";

const Accordion = ({ children, onChange, isOpen, label }) => {
  const onChangeHandler = () => {
    onChange && onChange(!isOpen);
  };

  return (
    <div className={styles.wrapper}>
      <span
        className={cx(styles.toggler, { [styles.active]: isOpen })}
        onClick={onChangeHandler}
      >
        {label}
      </span>
      <div
        className={cx(styles.panel, {
          [styles.active]: isOpen
        })}
      >
        <div className={styles.contentWrapper}>{children}</div>
      </div>
    </div>
  );
};

Accordion.propTypes = {
  isOpen: PropTypes.bool.isRequired,
  onChange: PropTypes.func,
  children: PropTypes.node,
  label: PropTypes.string.isRequired
};

Accordion.defaultProps = {
  isOpen: false,
  children: null,
  label: "Accordion"
};

export default Accordion;

Styling the accordion component

The major part of this component is its style.

First, in the toggle button we have to provide an indicator to show the state of the component, so we have placed an unicode character of +, - based on the state.

Second, as the display area should be expanding on the open state. We cannot hide it or make it visibility hidden because it will give that feel of expanding, instead we minimize its height and then increase it.

Below is the complete CSS code of our component.

//index.module.css
.toggler {
  position: relative;
  display: block;
  background-color: #f9f9f9;
  color: #444;
  cursor: pointer;
  padding: 8px;
  width: 100%;
  text-align: left;
  border: none;
  outline: none;
  transition: 0.4s;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
}

.toggler:after {
  content: "\02795";
  position: absolute;
  right: 10px;
  font-size: 10px;
  top: 48%;
  transform: translateY(-50%);
}

.toggler.active:after {
  content: "\2796";
}

.panel {
  transition: 0.4s;
  background-color: #90a4ae;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  overflow: hidden;
  max-height: 0;  //Keep the panel hidden
}

.panel > .contentWrapper {
  padding: 10px;
}

.wrapper {
  padding: 5px 10px;
}

//Shows the panel and adjusts the height accordingly
.panel.active {
  max-height: 100vh;
}

Input

import React, { Component } from "react";
import Accordion from "./index";

class AccordionTest extends Component {
  state = {
    isOpen: true
  };

  onChange = isOpen => {
    this.setState({
      isOpen
    });
  };

  render() {
    const { isOpen } = this.state;
    return (
      <Accordion isOpen={isOpen} onChange={this.onChange}>
        Lorem Ipsum is simply dummy text of the printing and typesetting
        industry. Lorem Ipsum has been the industry's standard dummy text ever
        since the 1500s, when an unknown printer took a galley of type and
        scrambled it to make a type specimen book. It has survived not only five
        centuries, but also the leap into electronic typesetting, remaining
        essentially unchanged. It was popularised in the 1960s with the release
        of Letraset sheets containing Lorem Ipsum passages, and more recently
        with desktop publishing software like Aldus PageMaker including versions
        of Lorem Ipsum.
      </Accordion>
    );
  }
}

export default AccordionTest;

Output

Open

React accordion open

Closed

React accordion closed