Avatar

Salut, I'm Julia.

Creating a React Accordion Component Using Just CSS

#react #typescript

2 min read

About a year ago now, I wrote a blog post on how to create an accordion component in React with Typescript and TailwindCSS. I recently needed to implement an accordion component again (this is why keeping a blog is so handy!), but this time in a codebase that doesn’t use TailwindCSS. Here’s a short follow-on to that post for how you’d create an accordion component if you were just using straight CSS. I'd recommend reading that post first if you're new to React, as it includes more detail on the order in which I built up the code.

Note that I’ve left most of the styling of the overall component out and focused on the important ones that effect the transition.

import React, { MutableRefObject, useRef, useState } from 'react'
import { appConfig } from '../appConfig'

interface AccordionProps {
  title: React.ReactNode
  content: React.ReactNode
}

export const Accordion: React.FC<AccordionProps> = ({ title, content }) => {
  const [showExtraContent, setShowExtraContent] = useState(false)
  const [height, setHeight] = useState('0px')

  const contentSpace = useRef(null) as MutableRefObject<HTMLDivElement>

  function toggleAccordion() {
		setShowExtraContent((previousState) => !previousState)
    setHeight(showExtraContent ? '0px' : `${contentSpace.current.scrollHeight}px`)
  }

  return (
    <div className="container">
      <button
        onClick={toggleAccordion}
      >
        <p>{title}</p>
        <img
          src={'/assets/img/icons/chevron-up.svg'}
          alt="Chevron icon"
          className={`${showExtraContent ? 'rotate' : null} arrow`}
        />
      </button>
      <div
        ref={contentSpace}
        style={{ maxHeight: `${height}` }}
        className="extra-content"
      >
        <div>{content}</div>
      </div>
    </div>
  )
}

Here are the corresponding CSS styles.

.container {
  display: flex;
  flex-direction: column;
}

.arrow {
  transition: 0.3s;
}

.rotate {
  transform: rotate(180deg);
}

.extra-content {
  overflow: hidden;
  transition: max-height 0.3s ease-in-out;
}

© 2016-2024 Julia Tan · Powered by Next JS.