How to Build an Accordion Component

I've been actively involved in the frontend development of the new AirSwap Member Dashboard app. AirSwap currently leverages a platform named Activate for user stake management. However, within a month, Activate will be phased out, prompting the development of a new app for $AST token stakers. A notable feature of this app is the Accordion component, which I'll discuss here, illustrating its creation with React, Radix, and TailwindCSS.

What is Radix-UI?

Radix-UI logo
Radix-UI logo

Radix-UI is an open-source library boasting ready-to-use components. These components are functional out-of-the-box but come unstyled, allowing for greater design flexibility. Radix effectively handles the underlying JavaScript, freeing you to focus on styling. If you’re without design guidelines, Radix's documentation provides some style suggestions.

You can install the Radix-UI accordion into your project with:

npm install @radix-ui/react-accordion
# or
yarn add @radix-ui/react-accordion

The documentation has many more components you can use.

Radix Accordion Component

Accordion component with multiple items. The first item is open
Accordion component with multiple items. The first item is open

Start by importing the Accordion:

import * as Accordion from '@radix-ui/react-accordion';

The Accordion has various segments, including Root, Item, Header, Trigger, and Content. For an in-depth understanding of these, refer to the Radix-UI Radix-UI documentation.

The Accordion supports numerous props for enhanced control. A notable one is the type prop, which allows selections of either "single" or "multiple", determining if one or multiple accordion items can be open simultaneously. For a comprehensive list of props, the Radix-UI docs have you covered. Below is my implementation of the component, with a brief explanation thereafter::

import * as RadixAccordion from '@radix-ui/react-accordion';
import { ReactNode, useState } from "react";
import { MdChevronRight } from "react-icons/md";

interface AccordionProps {
  rootStyles: string;
  type?: "single" | "multiple";
  itemId: string;
  trigger: ReactNode;
  content: ReactNode;
}

export const Accordion = ({
  rootStyles,
  type = "multiple",
  itemId,
  trigger,
  content,
}: AccordionProps) => {
const [isAccordionOpen, setIsAccordionOpen] = useState<boolean>(false);
const toggleAccordion = () => setIsAccordionOpen((isAccordionOpen) => !isAccordionOpen);

return (
    <RadixAccordion.Root className={rootStyles} type={type}>
      <RadixAccordion.Item value={itemId} className="flex flex-col overflow-hidden">
        <RadixAccordion.Header>
          <div className="flex border p-2">
            {trigger}
            <RadixAccordion.Trigger />
              <div onClick={toggleAccordion}>
                  {!isAccordionOpen ? (
                    <MdChevronRight size={32} className="rotate-90 transition-transform duration-150"
                  />
                ) : (
                  <MdChevronRight size={32} className="-rotate-90 transition-transform duration-150"
                  />
                )}
              </div>
            </RadixAccordion.Trigger>  
          </div>
        </RadixAccordion.Header>
        <RadixAccordion.Content>{content}</RadixAccordion.Content>
      </RadixAccordion.Item>
    </RadixAccordion.Root>
  );
};

Breaking Down the Code:

TypeScript Types (AccordionProps):

This interface encapsulates a set of props, catering to scenarios where the Accordion component might be invoked multiple times with varied data.

  • rootStyles: Directs styling for the Root component, accepting TailwindCSS classes.

  • type: Specifies if the accordion operates in “single” or “multiple” mode.

  • itemId: A unique identifier for the Item.

  • trigger: It's positioned outside of the <RadixAccordion.Trigger /> to ensure only the MdChevronRight icon, when clicked, initiates the accordion's action.

  • content: A ReactNode type defining the content within <RadixAccordion.Content> nodes.

Arrow Icon Behavior

Clicking the arrow icon (MdChevronRight) toggles the accordion's state. The isAccordionOpen state starts as false, with the accordion defaulting to a closed position.

Design Philosophy

The Accordion is intentionally kept versatile, with trigger and content props as ReactNode, facilitating potential reuse. Avoiding excessive inline styling makes the component more maintainable.

Conclusion

My experience with Radix-UI was enlightening, and I foresee further engagements. While I've worked with component libraries like Chakra-UI that offer pre-styled components, the customization process can be daunting, especially with specific brand guidelines in play.

Feel free to share your thoughts or questions regarding my Accordion component implementation below!

GitHub: https://github.com/airswap/airswap-voter-rewards/blob/main/src/features/common/Accordion.tsx.

Accordion component with all items closed
Accordion component with all items closed
Subscribe to starrdev
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.