How to Build Accordion In React From Scratch Without Using Any External Library

Subscribe to my newsletter and never miss my upcoming articles

As you might be aware, there are many applications of accordions like displaying a list of FAQs, various menus and submenus, for displaying various locations of a particular company and so on.

So In this article, we'll see how to build an accordion in React completely from scratch in a step-by-step way without using any external library.

We will be using React Hooks syntax for building this application in React. So if you're new to React Hooks, check out my Introduction to React Hooks article to learn the basics of Hooks.

You can see the live demo of the application here.

So let's get started.

Initial Project Setup

Create a new project using create-react-app

npx create-react-app react-accordion-demo

Once the project is created, delete all files from the src folder and create index.js, App.js, and styles.css files inside the src folder. Also, create a new folder with the name utils inside the src folder.

Open styles.css file and add the contents from here inside it.

How to Create the Initial Pages

Open src/App.js file and add the following content inside it:

import React from 'react';

const App = () => {
  const accordionData = {
    title: 'Section 1',
    content: `Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis sapiente
      laborum cupiditate possimus labore, hic temporibus velit dicta earum
      suscipit commodi eum enim atque at? Et perspiciatis dolore iure
      voluptatem.`
  };

  const { title, content } = accordionData;

  return (
    <React.Fragment>
      <h1>React Accordion Demo</h1>
      <div className="accordion">
        <div className="accordion-item">
          <div className="accordion-title">
            <div>{title}</div>
            <div>+</div>
          </div>
          <div className="accordion-content">{content}</div>
        </div>
      </div>
    </React.Fragment>
  );
};

export default App;

Here, we're using accordionData object properties for displaying the accordion content.

For the content property we're using ES6 template literal syntax (``) so we can spread the content across multiple lines and we've used a dummy lorem ipsum text.

Now, open src/index.js file and add the following content inside it:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './styles.css';

ReactDOM.render(<App />, document.getElementById('root'));

Now, If you run the application using yarn start command from the terminal, you will see the following screen:

Initial Accordion

How to Open and Close Accordion

As you can see above, we're displaying a single section as a part of the accordion but by default, the accordion is expanded and we can't close it. So let's add functionality to open and close the accordion.

Add a new state inside the component as shown below:

const [isActive, setIsActive] = useState(false);

Here, we've defined isActive state and based on that, we'll hide or show the accordion content.

Also import the useState hook at the top of the file:

import React, { useState } from 'react';

Now, for the div with class accordion-title, add the onClick handler like this:

<div className="accordion">
  <div className="accordion-item">
    <div
      className="accordion-title"
      onClick={() => setIsActive(!isActive)}
    >
      <div>{title}</div>
      <div>{isActive ? '-' : '+'}</div>
    </div>
    {isActive && <div className="accordion-content">{content}</div>}
  </div>
</div>

Here, we're inverting the isActive state value when we click on the accordion-title div. If the value of isActive is false, we're setting it to true and vice-versa.

We're also showing the + or - sign depending on the value of isActive using the ternary operator.

And If the isActive state value is true then only we're showing the accordion content as shown below:

{isActive && <div className="accordion-content">{content}</div>}

Now, If you check the application, you will see the following screen:

Open and close accordion

As you can see, initially, the accordion is closed and on clicking on the title, the accordion opens and we can click on it again to close it.

How to add Multiple Sections in Accordion

This is working fine for a single section of the accordion but If we have multiple sections, It will not be good to copy-paste the same JSX code again and again with different content.

So let's create a separate component to just display the accordion and based on the number of sections, we'll loop over the component to display multiple sections.

Create a new Accordion.js file inside the src folder and add the following contents inside it:

import React, { useState } from 'react';

const Accordion = ({ title, content }) => {
  const [isActive, setIsActive] = useState(false);

  return (
    <div className="accordion-item">
      <div className="accordion-title" onClick={() => setIsActive(!isActive)}>
        <div>{title}</div>
        <div>{isActive ? '-' : '+'}</div>
      </div>
      {isActive && <div className="accordion-content">{content}</div>}
    </div>
  );
};

export default Accordion;

Here, we've moved the state and accordion-item div from the App.js file into Accordion.js file and we're passing the dynamic title and content props to the component using ES6 destructuring syntax like this:

const Accordion = ({ title, content }) => {

Now, open App.js file and replace it with the following content:

import React from 'react';
import Accordion from './Accordion';

const App = () => {
  const accordionData = [
    {
      title: 'Section 1',
      content: `Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis sapiente
      laborum cupiditate possimus labore, hic temporibus velit dicta earum
      suscipit commodi eum enim atque at? Et perspiciatis dolore iure
      voluptatem.`
    },
    {
      title: 'Section 2',
      content: `Lorem ipsum, dolor sit amet consectetur adipisicing elit. Mollitia veniam
      reprehenderit nam assumenda voluptatem ut. Ipsum eius dicta, officiis
      quaerat iure quos dolorum accusantium ducimus in illum vero commodi
      pariatur? Impedit autem esse nostrum quasi, fugiat a aut error cumque
      quidem maiores doloremque est numquam praesentium eos voluptatem amet!
      Repudiandae, mollitia id reprehenderit a ab odit!`
    },
    {
      title: 'Section 3',
      content: `Sapiente expedita hic obcaecati, laboriosam similique omnis architecto ducimus magnam accusantium corrupti
      quam sint dolore pariatur perspiciatis, necessitatibus rem vel dignissimos
      dolor ut sequi minus iste? Quas?`
    }
  ];

  return (
    <div>
      <h1>React Accordion Demo</h1>
      <div className="accordion">
        {accordionData.map(({ title, content }) => (
          <Accordion title={title} content={content} />
        ))}
      </div>
    </div>
  );
};

export default App;

Here, we've converted the accordionData from object to array of objects and we're looping over it using the array map method and passing the corresponding title and content to the Accordion component.

Now, If you check the application, you will see the three sections getting displayed and we can open and close each section as shown below:

final working accordion

Code Refactoring

So as you can see, by just moving out the accordion section into a separate component and passing the dynamic content as props, we're successfully able to create a working version of an accordion from scratch.

It's a better practice to keep the static data in a separate file. So let's move the accordionData array to a different file and import it into App.js file.

Create a new file content.js inside the utils folder and add the following contents inside it:

export const accordionData = [
  {
    title: 'Section 1',
    content: `Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis sapiente
    laborum cupiditate possimus labore, hic temporibus velit dicta earum
    suscipit commodi eum enim atque at? Et perspiciatis dolore iure
    voluptatem.`
  },
  {
    title: 'Section 2',
    content: `Lorem ipsum, dolor sit amet consectetur adipisicing elit. Mollitia veniam
    reprehenderit nam assumenda voluptatem ut. Ipsum eius dicta, officiis
    quaerat iure quos dolorum accusantium ducimus in illum vero commodi
    pariatur? Impedit autem esse nostrum quasi, fugiat a aut error cumque
    quidem maiores doloremque est numquam praesentium eos voluptatem amet!
    Repudiandae, mollitia id reprehenderit a ab odit!`
  },
  {
    title: 'Section 3',
    content: `Sapiente expedita hic obcaecati, laboriosam similique omnis architecto ducimus magnam accusantium corrupti
    quam sint dolore pariatur perspiciatis, necessitatibus rem vel dignissimos
    dolor ut sequi minus iste? Quas?`
  }
];

Now, open App.js file and replace it with the following content:

import React from 'react';
import Accordion from './Accordion';
import { accordionData } from './utils/content';

const App = () => {
  return (
    <div>
      <h1>React Accordion Demo</h1>
      <div className="accordion">
        {accordionData.map(({ title, content }) => (
          <Accordion title={title} content={content} />
        ))}
      </div>
    </div>
  );
};

export default App;

Here, we've just imported the static data from the external file and removed it from the App.js file.

So now, the code looks clean and easy to understand and the functionality is working as before.

final working accordion

Closing points

We're done building out the functionality of the App.

You can find the complete GitHub source code for this application in this repository.

Thanks for reading!

Want to learn all ES6+ features in detail including let and const, promises, various promise methods, array and object destructuring, arrow functions, async/await, import and export and a whole lot more from scratch?

Check out my Mastering Modern JavaScript book. This book covers all the pre-requisites for learning React and helps you to become better at JavaScript and React.

Check out free preview contents of the book here.

Also, you can check out my free Introduction to React Router course to learn React Router from scratch.

Want to stay up to date with regular content regarding JavaScript, React, Node.js? Follow me on LinkedIn.

Rikard's photo

Nice! Just a quick reminder using native HTML elements is the easiest way to create an accessible accordion – no JS required!

<details>
<summary>Section 1</summary>
Hello this is my section. 
</details>

Example of the above code (edit: Hashnode doesn't seem to use the same renderer for "preview" and after posting, so I had to take a screenshot of it)

image.png

See this for more details: css-tricks.com/quick-reminder-that-details-..

Yogesh Chavan's photo

This is so cool. I was not aware of this. Thank you 🙂

Catalin's Tech's photo

A nice, detailed article! I should replicate this with Vue. 👀

Yogesh Chavan's photo

Catalin Pit haha. Thank you so much 🙂

Christiaan Kras's photo

One big issue with this is that this doesn't provide an accessible accordion. You also don't need to implement this in React since HTML provides the details and summary elements which give you native accordion functionality which is accessible as well, for free!

Yogesh Chavan's photo

Christiaan Kras Thanks for the information