Accordion

A set of collapsible panels with headings.

We offer standard (5-7 days), express (2-3 days), and overnight shipping. Free shipping on international orders.

"use client"

import { Accordion as AccordionPrimitive } from "@animate-ui/react/primitives/base-ui/accordion"
import { cn } from "cn"

import { ArrowDown01Icon } from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"

function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
  return (
    <AccordionPrimitive.Root
      data-slot="accordion"
      className={cn("flex w-full flex-col", className)}
      {...props}
    />
  )
}

function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
  return (
    <AccordionPrimitive.Item
      data-slot="accordion-item"
      className={cn("not-last:border-b", className)}
      {...props}
    />
  )
}

function AccordionTrigger({
  className,
  children,
  ...props
}: AccordionPrimitive.Trigger.Props) {
  return (
    <AccordionPrimitive.Header className="flex">
      <AccordionPrimitive.Trigger
        data-slot="accordion-trigger"
        className={cn(
          "focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-lg py-2.5 text-left text-sm font-medium hover:underline focus-visible:ring-3 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none aria-disabled:pointer-events-none aria-disabled:opacity-50",
          className
        )}
        {...props}
      >
        {children}
        <HugeiconsIcon icon={ArrowDown01Icon} strokeWidth={2}
          data-slot="accordion-trigger-icon"
          className="pointer-events-none shrink-0 transition-transform duration-350 ease-[cubic-bezier(0.215,0.61,0.355,1)] group-aria-expanded/accordion-trigger:rotate-180"
        />
      </AccordionPrimitive.Trigger>
    </AccordionPrimitive.Header>
  )
}

function AccordionContent({
  className,
  children,
  ...props
}: AccordionPrimitive.Panel.Props) {
  return (
    <AccordionPrimitive.Panel
      data-slot="accordion-content"
      className="text-sm overflow-hidden"
      {...props}
    >
      <div
        className={cn(
          "pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
          className
        )}
      >
        {children}
      </div>
    </AccordionPrimitive.Panel>
  )
}

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

export default function AccordionDemo({
  multiple = false,
}: {
  multiple?: boolean
}) {
  return (
    <Accordion defaultValue={["shipping"]} multiple={multiple} className="max-w-lg">
      <AccordionItem value="shipping">
        <AccordionTrigger>What are your shipping options?</AccordionTrigger>
        <AccordionContent>
          We offer standard (5-7 days), express (2-3 days), and overnight
          shipping. Free shipping on international orders.
        </AccordionContent>
      </AccordionItem>
      <AccordionItem value="returns">
        <AccordionTrigger>What is your return policy?</AccordionTrigger>
        <AccordionContent>
          Returns accepted within 30 days. Items must be unused and in original
          packaging. Refunds processed within 5-7 business days.
        </AccordionContent>
      </AccordionItem>
      <AccordionItem value="support">
        <AccordionTrigger>How can I contact customer support?</AccordionTrigger>
        <AccordionContent>
          Reach us via email, live chat, or phone. We respond within 24 hours
          during business days.
        </AccordionContent>
      </AccordionItem>
    </Accordion>
  )
}

Installation

Install the packages

npm install @animate-ui/react motion
pnpm add @animate-ui/react motion
yarn add @animate-ui/react motion
bun add @animate-ui/react motion

Add the shadcn/ui component

npx shadcn@latest add accordion
pnpm dlx shadcn@latest add accordion
yarn dlx shadcn@latest add accordion
bun x shadcn@latest add accordion

Animate it

The CLI finds your shadcn/ui components through components.json, swaps their primitive imports for Animate UI's and removes the CSS animation classes Motion replaces.

npx @animate-ui/cli@latest animate accordion
pnpm dlx @animate-ui/cli@latest animate accordion
yarn dlx @animate-ui/cli@latest animate accordion
bun x @animate-ui/cli@latest animate accordion

Usage

Import the primitive from Animate UI instead of Base UI. Everything else stays the same.

import { Accordion } from '@animate-ui/react/primitives/base-ui/accordion';

export function FAQ() {
  return (
    <Accordion.Root defaultValue={['item-1']}>
      <Accordion.Item value="item-1">
        <Accordion.Header>
          <Accordion.Trigger>Is it accessible?</Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Panel>
          Yes. It adheres to the WAI-ARIA design pattern.
        </Accordion.Panel>
      </Accordion.Item>
    </Accordion.Root>
  );
}

Using the @base-ui/react umbrella import instead? The barrel mirrors it: import { Accordion } from '@animate-ui/react/primitives/base-ui'.

Animation

  • Panel unfolds: its height grows from 0 while the content fades in from a 2px blur, over 350ms on a soft ease-out. While it moves, its bottom edge fades (over 16px, or over the part still hidden): the content never meets what follows it, an item's border, with a hard cut. It folds back the same way, in reverse, then unmounts.
  • Opening another item while one is closing retargets smoothly.
  • An item open by default animates open on mount (page load), as in v1.
  • Motion animates the panel itself: shadcn/ui's h-(--accordion-panel-height) data-starting-style:h-0 data-ending-style:h-0 classes are no longer needed (the CLI removes them). --accordion-panel-height and --accordion-panel-width read auto.
  • keepMounted and hiddenUntilFound, on the Root or a Panel, keep closed panels in the DOM: they collapse, then get Base UI's hidden (or hidden="until-found", which find-in-page can reveal; a panel revealed that way opens at once, as in Base UI).
  • An onValueChange that calls eventDetails.cancel() still cancels the change.
  • <AnimateUIProvider tempo> paces both transitions.
  • Under reduced motion, the height changes at once; the fade and the blur still play.

API Reference

Every part takes exactly the props of Base UI's Accordion. Animate UI adds the animation props below.

Root, Item, Header, Trigger

Base UI props only. They are not animated.

Panel

Base UI props, plus:

PropTypeDefault
transition?
Transition{ duration: 0.35, ease: [0.215, 0.61, 0.355, 1] }
exitTransition?
Transitiontransition

A render element (or function) of your own is made animatable with motion.create.

Built by Skyleen. The source code is available on GitHub.

Last updated: 10/18/2026

On this page