The Accordion component allows users to show and hide sections of related content. It supports both single and multiple item expansion modes, with smooth animations and full keyboard navigation.
Installation
npx shadcn@latest add @eo-n/accordion
Copy the component code
Copy and paste the Accordion component code into your project at components/ui/accordion.tsx.components/ui/accordion.tsx
"use client";
import * as React from "react";
import { Accordion as AccordionPrimitive } from "@base-ui/react";
import { ChevronDown } from "lucide-react";
import { cn } from "@/lib/utils";
function Accordion({
className,
multiple = false,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return (
<AccordionPrimitive.Root
data-slot="accordion"
multiple={multiple}
className={cn("flex w-full max-w-lg flex-col justify-center", className)}
{...props}
/>
);
}
function AccordionItem({
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return (
<AccordionPrimitive.Item
data-slot="accordion-item"
className={cn(
"border-b last:border-b-0 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
/>
);
}
function AccordionTrigger({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
return (
<AccordionPrimitive.Header data-slot="accordion-header">
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
className={cn(
"focus-visible:border-ring focus-visible:ring-ring/50 flex w-full cursor-pointer items-center rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-panel-open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<ChevronDown className="text-muted-foreground ml-auto size-4 shrink-0 transition-transform duration-300 ease-out" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
);
}
function AccordionContent({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Panel>) {
return (
<AccordionPrimitive.Panel
data-slot="accordion-content"
className="h-[var(--accordion-panel-height)] overflow-hidden text-left text-sm transition-[height,opacity] duration-300 ease-out data-[ending-style]:h-0 data-[ending-style]:opacity-0 data-[starting-style]:h-0 data-[starting-style]:opacity-0"
{...props}
>
<div className={cn("pt-0 pb-4", className)}>{children}</div>
</AccordionPrimitive.Panel>
);
}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
Update imports
Update the import paths to match your project setup.
Usage
Import the Accordion components and compose them together:
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
<Accordion>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern and is fully keyboard
navigable. The component is built with proper focus management and ARIA
attributes for screen reader compatibility.
</AccordionContent>
</AccordionItem>
</Accordion>
Component API
Accordion
The root container component that manages accordion state.
Whether multiple accordion items can be open at the same time. When false, opening one item automatically closes others.
The value(s) of the item(s) that should be open by default.
The controlled value of the open item(s).
onValueChange
(value: string | string[]) => void
Callback fired when the open items change.
Additional CSS classes to apply.
AccordionItem
Container for an individual accordion item.
Unique identifier for this accordion item.
Whether the accordion item is disabled.
Additional CSS classes to apply to the item.
AccordionTrigger
The clickable header that toggles the accordion item.
Additional CSS classes to apply to the trigger.
AccordionContent
The collapsible content panel.
Additional CSS classes to apply to the content wrapper.
Examples
Single Item Open (Default)
By default, only one accordion item can be open at a time:
<Accordion defaultValue="item-1">
<AccordionItem value="item-1">
<AccordionTrigger>What is EoN UI?</AccordionTrigger>
<AccordionContent>
EoN UI is a modern React component library built with accessibility and
customization in mind.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>How do I install it?</AccordionTrigger>
<AccordionContent>
You can install components using the CLI or copy them manually into your project.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it free?</AccordionTrigger>
<AccordionContent>
Yes, EoN UI is completely free and open source.
</AccordionContent>
</AccordionItem>
</Accordion>
Multiple Items Open
Allow multiple items to be expanded simultaneously:
<Accordion multiple defaultValue={["item-1", "item-2"]}>
<AccordionItem value="item-1">
<AccordionTrigger>Features</AccordionTrigger>
<AccordionContent>
Built with TypeScript, fully accessible, and customizable.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Documentation</AccordionTrigger>
<AccordionContent>
Comprehensive docs with examples and API references.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Support</AccordionTrigger>
<AccordionContent>
Community support through GitHub discussions.
</AccordionContent>
</AccordionItem>
</Accordion>
Disabled Item
Disable specific accordion items:
<Accordion>
<AccordionItem value="item-1">
<AccordionTrigger>Available Item</AccordionTrigger>
<AccordionContent>This item is available and can be opened.</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2" disabled>
<AccordionTrigger>Disabled Item</AccordionTrigger>
<AccordionContent>This content cannot be accessed.</AccordionContent>
</AccordionItem>
</Accordion>
Controlled Accordion
Fully control the accordion state:
function ControlledAccordion() {
const [openItems, setOpenItems] = React.useState(["item-1"]);
return (
<Accordion multiple value={openItems} onValueChange={setOpenItems}>
<AccordionItem value="item-1">
<AccordionTrigger>First Item</AccordionTrigger>
<AccordionContent>First content</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Second Item</AccordionTrigger>
<AccordionContent>Second content</AccordionContent>
</AccordionItem>
</Accordion>
);
}
Accessibility
The Accordion component is built on Base UI’s accessible Accordion primitive, which:
- Implements the ARIA accordion design pattern
- Supports full keyboard navigation (Tab, Space, Enter, Arrow keys)
- Properly manages focus between triggers and content
- Uses appropriate ARIA attributes for screen readers
- Supports disabled states
- Includes smooth animations for expand/collapse transitions
For more details, see the Base UI Accordion documentation.