Build React Accordion Without Any Libraries
Build React Accordion Without Any Libraries

A lot of people are still looking for some libraries to implement simple components inside React. This is not correct. This is why in this post we will implement accordion just for our needs so you understand how we can implement such component on your own.

Finished project

This is the accordion that we need to implement. It's a bunch of sections with title and description. We can activate just one section and when it happens all other sections are closed and we can read the content of this one section.

The project

I already generated an empty React project and I prepared data for our component.

const accordionData = [
  {
    title: "Section 1",
    content: `
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id erat sit amet arcu luctus pharetra. Cras scelerisque dolor non justo dignissim efficitur. Nunc non molestie nunc, venenatis maximus ante. Vestibulum mattis faucibus scelerisque. Nulla tempus semper congue. Phasellus maximus iaculis eleifend. Praesent elementum risus augue, eu viverra eros pulvinar sagittis. Aliquam ullamcorper vel metus at ultricies. Phasellus sollicitudin nibh lacus, nec malesuada felis dapibus eu. Suspendisse ut condimentum felis, vitae accumsan nunc. Nunc non bibendum diam, eget molestie metus. Nam orci sem, porttitor sed libero ut, rutrum tristique nulla. Praesent et odio posuere diam ultricies luctus nec eu nibh. Cras sit amet sagittis erat. Nulla viverra rhoncus magna, vitae aliquet ex consectetur at.
    `,
  },
  {
    title: "Section 2",
    content: `
      Aliquam vel libero et tortor sagittis condimentum. Nam id varius turpis, id pharetra eros. Mauris purus tortor, mattis quis eros in, molestie pharetra lorem. Morbi viverra urna purus, nec ornare purus aliquet et. Curabitur tempus nulla id leo eleifend, sit amet lobortis libero interdum. Proin nulla neque, imperdiet nec metus in, volutpat accumsan sem. Curabitur imperdiet et turpis at condimentum. Nunc nec quam fringilla, porta elit nec, pellentesque ligula.
    `,
  },
  {
    title: "Section 3",
    content: `
      Sed lobortis rutrum dui ut consequat. Vestibulum sollicitudin orci eget risus volutpat, quis congue neque pharetra. Nulla sed justo commodo tellus tincidunt lobortis. Nunc tortor augue, consequat eu odio nec, accumsan accumsan lorem. Suspendisse maximus ultricies turpis, consequat mollis diam tempor ut. Fusce a elementum est. Nulla odio elit, malesuada eu leo sit amet, malesuada accumsan magna. Maecenas molestie bibendum lorem, et ullamcorper dui euismod eget. Proin eget dui dapibus lacus fermentum mattis vitae iaculis lectus. Pellentesque in risus in nibh commodo imperdiet. Morbi at tempus dui. Mauris pellentesque mauris id erat blandit, ac ultrices metus efficitur. Donec porttitor neque eget elit volutpat gravida.
    `,
  },
];

This is just an array with objects. Every object is a section with title and content.

Component creation

Now the question is what props do we need to pass to accordion? And actually this array is enough to render an accordion and our accordion will be fully encapsulated.

const App = () => {
  return (
    <div>
      <h1>Hello monsterlessons</h1>
      <Accordion sections={accordionData} />
    </div>
  );
};

Here is how we can use our Accordion component and provide sections inside.

const Accordion = ({ sections }) => {
  return (
    <div>
      {sections.map((section, index) => (
        <div key={index}>{section.title}</div>
      ))}
    </div>
  );
};

Here is a basic version of our accordion. We get our sections as a prop and render them in the loop. Now I want to create an additional component AccordionSection with the logic of a single section.

const AccordionSection = ({section}) => {
  return (
    <div>
      <div>{section.title}</div>
      <div>{section.description}</div>
    </div>
  )
}

const Accordion = ({ sections }) => {
  return (
    <div>
      {sections.map((section, index) => (
        <AccordionSection section={section} key={index}/>
      ))}
    </div>
  );
};

Here we just moved our markup to AccordionSection and provided a section inside.

It is time to add our active section so we just render a content of activated section.

const AccordionSection = ({section, isActiveSection}) => {
  return (
    <div>
      <div>{section.title}</div>
      {isActiveSection && <div>{section.description}</div>}
    </div>
  )
}

const Accordion = ({ sections }) => {
  const [activeIndex, setActiveIndex] = useState(0)
  return (
    <div>
      {sections.map((section, index) => (
        <AccordionSection
          section={section}
          key={index}
          isActiveSection={index === activeIndex}
        />
      ))}
    </div>
  );
};

Highlighting active section

Now we must style our accordion so it looks better.

const accordionStyles = {
  maxWidth: "600px",
};

const accordionTitleStyles = {
  display: "flex",
  justifyContent: "space-between",
  cursor: "pointer",
  padding: "5px",
  background: "#21aeca",
};

const accordionContentStyles = {
  padding: "5px",
  background: "#39b9d2",
};

const AccordionSection = ({
  section,
  isActiveSection,
  setActiveIndex,
  sectionIndex,
}) => {
  return (
    <div>
      <div style={accordionTitleStyles}>
        <div>{section.title}</div>
        <div>{isActiveSection ? "-" : "+"}</div>
      </div>
      {isActiveSection && (
        <div style={accordionContentStyles}>{section.content}</div>
      )}
    </div>
  );
};

const Accordion = ({ sections }) => {
  const [activeIndex, setActiveIndex] = useState(0);
  return (
    <div style={accordionStyles}>
      {sections.map((section, index) => (
        <AccordionSection
          section={section}
          key={index}
          isActiveSection={index === activeIndex}
          setActiveIndex={setActiveIndex}
          sectionIndex={index}
        />
      ))}
    </div>
  );
};

Finished project

As you can see in browser it looks much better.

Activating a section

Now we just need to implement activation of the section. For this we must provide setActiveIndex inside and call it accordingly.

const AccordionSection = ({
  section,
  isActiveSection,
  setActiveIndex,
  sectionIndex,
}) => {
  const toggleSection = () => {
    const nextIndex = isActiveSection ? null : sectionIndex;
    setActiveIndex(nextIndex);
  };
  return (
    <div>
      <div style={accordionTitleStyles} onClick={toggleSection}>
        <div>{section.title}</div>
        <div>{isActiveSection ? "-" : "+"}</div>
      </div>
      {isActiveSection && (
        <div style={accordionContentStyles}>{section.content}</div>
      )}
    </div>
  );
};

Here we set sectionIndex to null if it is already open or set it to activeIndex if it is not.

And actually if you are interested to learn how to build real React project from start to the end make sure to check my React hooks course.

📚 Source code of what we've done