Accordion
A vertically stacked set of interactive headings that each reveal an associated section of content.
Installation
npx shadcn@latest add @redpanda/accordionpnpm dlx shadcn@latest add @redpanda/accordionyarn dlx shadcn@latest add @redpanda/accordionbunx shadcn@latest add @redpanda/accordionnubx shadcn@latest add @redpanda/accordionaube dlx shadcn@latest add @redpanda/accordionUsage
Basic Example
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/redpanda-ui/accordion';
<Accordion type="single" collapsible> <AccordionItem value="item-1"> <AccordionTrigger>What is Redpanda?</AccordionTrigger> <AccordionContent> Redpanda is a streaming data platform that is Kafka-compatible but simpler to operate and 10x faster. </AccordionContent> </AccordionItem> <AccordionItem value="item-2"> <AccordionTrigger>How does it work?</AccordionTrigger> <AccordionContent> Redpanda is built from the ground up in C++ and designed to be simple, powerful, and cost-efficient. </AccordionContent> </AccordionItem> <AccordionItem value="item-3"> <AccordionTrigger>Is it production ready?</AccordionTrigger> <AccordionContent>Yes, Redpanda is production-ready and used by hundreds of companies worldwide.</AccordionContent> </AccordionItem></Accordion>;Multiple Items Open
<Accordion type="multiple"> <AccordionItem value="features"> <AccordionTrigger>Features</AccordionTrigger> <AccordionContent> <ul className="list-disc pl-6 space-y-1"> <li>Kafka API compatible</li> <li>10x faster performance</li> <li>Built-in schema registry</li> <li>Tiered storage support</li> </ul> </AccordionContent> </AccordionItem> <AccordionItem value="pricing"> <AccordionTrigger>Pricing</AccordionTrigger> <AccordionContent> Redpanda offers both open source and cloud options with flexible pricing models. </AccordionContent> </AccordionItem></Accordion>Without Chevron
<Accordion type="single" collapsible> <AccordionItem value="no-chevron"> <AccordionTrigger chevron={false}>No Chevron Icon</AccordionTrigger> <AccordionContent>This accordion item doesn't display the chevron indicator.</AccordionContent> </AccordionItem></Accordion>Contained Variant
The contained variant provides a more visually distinct card-like appearance with rounded borders and background colors:
<Accordion type="single" defaultValue="item-1" collapsible variant="contained" className="max-w-[500px] w-full"> <AccordionItem value="item-1"> <AccordionTrigger>What is Redpanda UI?</AccordionTrigger> <AccordionContent> Redpanda UI is an open-source distribution of React components built with TypeScript and Tailwind CSS. </AccordionContent> </AccordionItem>
<AccordionItem value="item-2"> <AccordionTrigger>How is it different from other libraries?</AccordionTrigger> <AccordionContent> Instead of installing via NPM, you copy and paste the components directly. This gives you full control to modify or customize them as needed. </AccordionContent> </AccordionItem>
<AccordionItem value="item-3"> <AccordionTrigger>Is Redpanda UI free to use?</AccordionTrigger> <AccordionContent> Absolutely! Redpanda UI is fully open-source. You can use, modify, and adapt it to fit your needs. </AccordionContent> </AccordionItem></Accordion>Controlled with Expand / Collapse All
Disabled Item
Contained with Custom Elements
The contained variant supports custom start and end elements for more complex layouts:
import { Circle, FileText, Settings } from 'lucide-react';import { Badge } from '@/components/badge';
<Accordion type="single" defaultValue="item-1" collapsible variant="contained" className="max-w-[500px] w-full"> <AccordionItem value="item-1"> <AccordionTrigger start={<Circle className="size-4 text-subtle" />} end={<Badge tone="success">Available</Badge>}> Accordion heading </AccordionTrigger> <AccordionContent> This is the accordion content that provides additional details when the accordion is expanded. </AccordionContent> </AccordionItem>
<AccordionItem value="item-2"> <AccordionTrigger start={<FileText className="size-4 text-subtle" />} end={ <Badge tone="default" variant="subtle"> In preview </Badge> } > Accordion heading </AccordionTrigger> <AccordionContent> This is the accordion content that provides additional details when the accordion is expanded. </AccordionContent> </AccordionItem>
<AccordionItem value="item-3"> <AccordionTrigger start={<Settings className="size-4 text-subtle" />} end={<Badge tone="success">Available</Badge>}> Accordion heading </AccordionTrigger> <AccordionContent> This is the accordion content that provides additional details when the accordion is expanded. </AccordionContent> </AccordionItem></Accordion>;When to use
Use an accordion when you need to display a large amount of content in a compact, organized way. This component is ideal for:
Use Accordion when:
- You have FAQ sections or help documentation
- Content can be grouped into clear, discrete sections
- Screen space is limited and you need to show/hide content
- Users typically need to focus on one section at a time
- You want to provide an overview with expandable details
Don’t use Accordion when:
- Users need to compare content across sections simultaneously
- The content sections are very short (use a simple list instead)
- Navigation between sections is the primary goal (use Tabs instead)
- The content doesn’t have a natural hierarchical structure
Anatomy
The Accordion component consists of several parts working together:
Accordion Container┌─────────────────────────────────────────────────┐│ AccordionItem (value="item-1") ││ ┌─────────────────────────────────────────────┐ ││ │ AccordionHeader │ ││ │ ┌─────────────────────────────────────────┐ │ ││ │ │ AccordionTrigger (clickable) │ │ ││ │ │ [Text Content] [ChevronDown Icon] ────┐ │ │ ││ │ └─────────────────────────────────────────┘ │ │ ││ └─────────────────────────────────────────────┘ │ ││ ┌─────────────────────────────────────────────┐ │ ││ │ AccordionContent (animated) │ │ ││ │ [Expandable content with CSS height transition] │ │ ││ └─────────────────────────────────────────────┘ │ │├─────────────────────────────────────────────────┤ ││ AccordionItem (value="item-2") │ ││ [Similar structure repeats...] │ │└─────────────────────────────────────────────────┘ │ │Context Flow: │AccordionItemContext ──────────────────────────────┘├── isOpen: boolean└── setIsOpen: (open: boolean) => voidComponent Hierarchy:
- Accordion (Root): Manages overall state and behavior (single vs multiple)
- AccordionItem: Individual accordion section with unique value
- AccordionHeader: Contains the clickable trigger (automatically included)
- AccordionTrigger: Clickable element that toggles the content visibility
- AccordionContent: Expandable content area with smooth animations
Key Interactions:
- State Management: Each AccordionItem tracks its open/closed state via context
- Animation: Base UI CSS transitions provide smooth height changes as the panel expands and collapses
- Accessibility: Built on Base UI primitives for keyboard navigation and screen readers
- Chevron Animation: Icon rotates 180° when content expands via a CSS transform transition
Props
Keyboard interactions
| Key | Action |
|---|---|
Tab to trigger |
Focus moves to the next accordion trigger. |
Enter / Space |
Toggles the focused item. |
ArrowDown |
Moves focus to the next trigger. Wraps at the end. |
ArrowUp |
Moves focus to the previous trigger. Wraps at the start. |
Home / End |
Jumps to first / last trigger. |
Accessibility
- Each
AccordionTriggerrenders inside anAccordionHeader(an<h3>by default). Override the tag viarenderonAccordionHeaderif the heading level doesn’t match your document outline. - Triggers expose
aria-expanded,aria-controls, and Base UI’s nativedata-panel-openattribute — CSS selectors likedata-[panel-open]:rotate-180keep working. - In
type="single"mode the currently-open item can always be collapsed again.
Component Props
Accordion
variant?'simple' | 'contained'
The visual style variant of the accordion
'simple' | 'contained''simple'AccordionTrigger
chevron?boolean
Whether to show the accordion chevron
booleantruestart?React.ReactNode
Optional element to display at the start of the trigger (e.g., icons)
React.ReactNodeundefinedend?React.ReactNode
Optional element to display at the end of the trigger before the chevron (e.g., badges)
React.ReactNodeundefinedDon’t delete from the DOM
The choice made (also Base UI’s default) is to remove the element from the DOM for accessibility and performance reasons. However, this may pose a problem for SEO. If you want your Accordion content to be taken into account by Google, replace the AccordionContent component with:
function AccordionContent({ className, children, ...props }: AccordionContentProps) { const { variant } = useAccordionItem();
return ( <AccordionPrimitive.Panel className="h-(--accordion-panel-height) overflow-hidden transition-[height] duration-200 ease-out data-[ending-style]:h-0 data-[starting-style]:h-0" data-slot="accordion-content" keepMounted {...props} > <div className={cn(accordionContentVariants({ variant }), className)}>{children}</div> </AccordionPrimitive.Panel> );}