How to Build an Accordion Component with React.js

    Omotosho Toheeb
    Share

    In this article, we’ll harness the full capabilities of React.js to create an accordion component — a user interface device that’s frequently used in web and mobile applications to arrange and show content in a user-friendly and space-efficient way.

    To get the most our of this article, you’ll need the following:

    The following video shows our finished accordion component.

    Table of Contents

    Project Setup

    We’ll be using React.js to create our accordion component. To use React.js, we’ll need to create a React environment, and we’ll do that via a command prompt.

    Open your terminal application and navigate to the desktop (or somewhere else if you prefer). Then run the following command to create your React app:

    npx create-react-app accordion-component
    

    Once the packages are installed, we’ll see something like the image below.

    What we see in the terminal once our app is created

    Now if we check our project folder, we’ll find a folder named /accordion-component/ with all the packages installed.

    Folder Structure

    Open the new /accordion-component/ folder in a code editor. Also open the React application in the browser. We can do that via the inbuilt terminal in our code editor by typing the command npm run start to run the application on the browser.

    Note: if you’re using Visual Studio, you can use the shortcut (ctrl + shift + `) to open up the terminal. If your code editor doesn’t have the feature of an inbuilt terminal, you can just run commands in the command prompt app.)

    Let’s next edit the unnecessary files and code blocks that will hinder the execution of our application. Firstly, open App.js and remove the whole header element that’s wrapped in the <div> element with a class name of App, so we now have an empty <div> element. Then open App.css and index.css and delete the contents of both files. (If you view the web page once more, you’ll see that it’s now blank, which is just what we want for now.)

    Next, let’s create a new folder called /AccordionComponent/ under the /src/ folder directory. Inside the /AccordionComponent/ folder, create a file called Accordion.js for the components and another file named AccordionData.js to store the text to be used for our accordion. Then go to the App.js file and import the Accordion.js file. After the file has been imported, we render it inside the <div> element like so:

    import './App.css';
    import Accordion from './AccordionComponent/Accordion';
    
    function App() {
     return (
      <div className="App">
       <Accordion />
      </div>
     );
    }
    
    export default App;
    

    After that’s done, go to the Accordion.js file and create a component called AccordionItem. Inside the return keyword, we’ll create a heading element with “Accordion” as the content (<h1>Accordion</h1>), and underneath that another component called Accordion. After doing that, we’ll render our AccordionItem component inside that of the main Accordion, making sure the rendered component is wrapped in a <div> element with a class name of container. We then export the main Accordion component. Now we have something like this:

    import React from 'react';
    
    //  accordionitem component
    const AccordionItem = () => {
      return(
        <h1>Accordion</h1>
      )
    }
    
    // main Accordion component
    const Accordion = () => {
     return (
      <div>
        <AccordionItem />
      </div>
     )
    }
    
    export default Accordion;
    

    If we view our web page, we’ll see our heading on the screen.

    We’ll next create an array of objects containing the questions and answers text inside the AccordionData.js file. By storing our accordion data in an array of objects, we ensure that the data is dynamically stored and the accordion component is reusable. Below is the accordion data. You can copy and paste it in your AccordionData.js file directly:

    const data = [
      {
       question: 'What are accordion components?',
       answer: 'Accordion components are user interface elements used for organizing and presenting content in a collapsible manner. They typically consist of a header, content, and an expand/collapse action.' ,
      },
      {
       question: 'What are they used for?',
       answer: 'They are commonly employed in various contexts, including FAQs, product descriptions, navigation menus, settings panels, and data tables, to save screen space and provide a structured and user-friendly interface for presenting information or options.',
      },
      {
       question: 'Accordion as a musical instrument',
       answer: 'The accordion is a musical instrument with a keyboard and bellows. It produces sound by air passing over reeds when the player expands or compresses the bellows, used in various music genres.',
      },
      {
       question: 'Can I create an accordion component with a different framework?',
       answer: 'Yes of course, it is very possible to create an accordion component with another framework.',
      }
     ];
    
     export default data;
    

    In the code above, we have an array of objects holding the data that will be displayed in our accordion component. The question property contains the question or header text, while the answer property contains the answer or content that appears when the question is clicked or expanded. Make sure to import the component in the Accordion.js file. That’s all for the AccordionData.js file.

    Accordion Component Layout

    Let’s create the layout of our accordion component.

    We first have to install react-icons to our project from the npm registry:

    npm install react-icons
    

    We also need to import useState and useRef hooks. We can do that by pasting this into the top of the file:

    import React, { useRef, useState } from 'react'
    

    The HTML structure will be rendered inside the AccordionItem component. We’ll pass four props into the AccordionItem component: question, answer, isOpen, and onClick.

    Let’s break down the props to see what they’ll be needed for:

    • question. This prop represents the text or content for the question part of the accordion item.
    • answer. This prop represents the text or content for the answer part of the accordion item.
    • isOpen. This prop is a Boolean that indicates whether the accordion item is currently open (expanded) or closed (collapsed). It controls whether the answer content is visible or hidden.
    • onClick. This prop is a callback function that gets executed when the user interacts with the accordion item. It’s usually used to toggle the isOpen state when the user clicks on the item to expand or collapse it.

    The AccordionComponent Body

    At the top of the Accordion.js file, make sure to import the arrow icon from the react-icons package, like this:

    import { RiArrowDropDownLine } from 'react-icons/ri'
    

    This will be the structure of a single accordion item:

    const AccordionItem = ({ question, answer, isOpen, onClick }) => {
     const contentHeight = useRef()
      return(
        <div className="wrapper" >
        <button className={`question-container ${isOpen ? 'active' : ''}`} onClick={onClick} >
         <p className='question-content'>{question}</p>
         <RiArrowDropDownLine className={`arrow ${isOpen ? 'active' : ''}`} /> 
        </button>
    
         <div ref={contentHeight} className="answer-container" style={
              isOpen
              ? { height: contentHeight.current.scrollHeight }
              : { height: "0px" }
             }>
          <p className="answer-content">{answer}</p>
         </div>
       </div>
      )
    }
    

    In this code snippet, the accordion item sits within a parent <div> with a class name wrapper. This structure allows for displaying a question and its answer in a collapsible manner.

    We store our useRef hook in a variable called contentHeight so it can be passed into the ref attribute of our answer-container element. We do that so we’ll be able to dynamically adjust the height of the container based on the answer content’s scroll height.

    Let’s break down the code structure.

    • Button element (<button>). This is the interactive part of the accordion item that users click to toggle the answer’s visibility. It has a class name question-container. The class name is conditionally set to active if the isOpen prop is true, which is used to style the button differently when the answer is open.

    • Question content. The question content consists of a <p> element with a class question-content. The text for the question is taken from the question prop.

    • Arrow Icon (<RiArrowDropDownLine />). An arrow icon used for toggling is displayed to the right of the question. The class name is conditionally set to active if the isOpen prop is true, which can be used to rotate or style the arrow differently when the answer is open.

    • Answer div. Following the <button>, there’s a <div> element with the class name answer-container. This div has an ref attribute set to the contentHeight variable, which allows it to measure its scrollHeight. The style attribute is used to dynamically set the height of this container based on whether the item is open or closed. When isOpen is true, it will have a height equal to its content’s scrollHeight, making the answer visible. When isOpen is false, it has a height of 0px, hiding the answer content.

    • Answer content. The answer content consists of a <p> element with class answer-content. The text for the answer is taken from the answer prop.

    Styling our Accordion Component

    Now that we’re done with the markup, let’s style our accordion component. The styling can be found in the code block below:

    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }
    
    body {
      background-color: #f2f2f2;
    }
    
    .container {
      max-width: 650px;
      width: 100%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
    }
    
    .wrapper {
      border-bottom: 1px solid black;
      overflow: hidden;
    }
    
    .wrapper .question-container {
      width: 100%;
      text-align: left;
      padding: 20px 10px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-weight: 500;
      font-size: 20px;
      background: transparent;
      border: none;
      cursor: pointer;
    }
    
    .question-container.active {
      color: #1db954;
      background-image: linear-gradient(90deg,transparent,rgba(0,0,0,0.04),transparent);
    }
    
    .wrapper .question-container:hover {
      background-image: linear-gradient(90deg,transparent,rgba(0,0,0,0.04),transparent);
    }
    
    .wrapper .arrow {
      font-size: 2rem;
      transition: .5s ease-in-out;
    }
    
    .arrow.active {
      rotate: 180deg;
      color: #1db954;
    }
    
    .wrapper .answer-container {
      padding: 0 1rem;
      transition: height .7s ease-in-out;
    }
    
    .wrapper .answer-content {
      padding: 1rem 0;
      font-size: 20px;
      font-style: italic;
    }
    

    As a result of the styling above, we now have the outline of our accordionItem. Now let’s import the data from our AccordionData file and declare the basic functionalities of our accordion component. We do that inside the main Accordion component.

    Main accordion component structure

    The code below defines the functional component named Accordion:

    const Accordion = () => {
     const [activeIndex, setActiveIndex] = useState(null);
    
     const handleItemClick = (index) => {
      setActiveIndex((prevIndex) => (prevIndex === index ? null : index));
     };
    
     return (
      <div className='container'>
        {data.map((item, index) => (
        <AccordionItem
         key={index}
         question={item.question}
         answer={item.answer}
         isOpen={activeIndex === index}
         onClick={() => handleItemClick(index)}
        />
       ))}
      </div>
     )
    };
    
    export default Accordion;
    

    The purpose of this component is to create the accordion-style interface that displays a list of items, each consisting of a question and its corresponding answer. The user can click on a question to expand or collapse its answer. Let’s break down the code step by step.

    • const [activeIndex, setActiveIndex] = useState(null);. This line sets a piece of component state using the useState hook. activeIndex represents the index of the currently active (open) accordion item, or null if no item is open. setActiveIndex is the function used to update this state.

    • const handleItemClick = (index) => { ... }. ThehandleItemClick function is responsible for handling clicks on accordion items. It takes an index parameter, which represents the index of the item that was clicked.

      Inside the function, setActiveIndex is called with a function that toggles the activeIndex state. If the clicked item’s index (index) matches the current active index (prevIndex), it sets activeIndex to null, effectively closing the item. If they don’t match, it sets activeIndex to the clicked item’s index, opening it.

      This approach ensures that only one accordion item can be opened at a time, because if we open one accordion item, it closes any previously opened accordion item.

    • The return statement. This component returns JSX that defines the structure of the accordion interface. The outermost <div> with the class name container is the container for all accordion items.

    • {data.map((item, index) => ( ... ))}. This code maps over an array called data that’s retrieved from the AccordionData.js file. For each item in the data array, it renders an AccordionItem component. The key prop is set to index to ensure each item has a unique key for React’s rendering optimization.

      The question, answer, isOpen, and onClick props are passed to the AccordionItem component. The question and answer props contain the text to be displayed for each item. The isOpen prop is set to true if the item’s index matches the currently active index (indicating that it should be open), and the onClick prop is a callback function that triggers the handleItemClick function when the item is clicked.

    • export default Accordion;. This line exports the Accordion component so that it can be imported and used in other parts of our application. We have previously rendered the component in our App.js file.

    In summary, the Accordion component manages the state of the currently active accordion item and uses this state to control the opening and closing behavior. It dynamically generates a list of AccordionItem components based on the data obtained, allowing users to interact with the accordion interface by clicking on each of the questions to reveal or hide their answers.

    Our Finished Product

    We now have a beautiful and fully functional accordion component! 🥳 🎉 The complete source code for this tutorial is available on CodeSandbox.

    Conclusion

    In this article, we’ve looked at how to utilize React.js to create a dynamic and user-friendly accordion component. Accordions are a common user interface element for neatly organizing and displaying content.

    We began by creating a React project, organizing the component, and styling it for a finished appearance. We went into the inner workings of the system, including state management and dealing with user interactions. In addition, for scalability and reusability, we covered the concept of storing the accordion data in another file.

    Hopefully you now have a solid understanding of how to develop a feature-rich accordion component with React.js. Happy coding!