---
title: Accordion
description: A vertically stacked set of interactive headings that each reveal an associated section of content.
icon: chevron-down
author:
  name: imskyleen
  url: https://github.com/imskyleen
mode: wide
---

<PoweredBy
  packages={[
    { name: 'Base UI', url: 'https://base-ui.com/react/components/accordion' },
    { name: 'Shadcn UI', url: 'https://ui.shadcn.com/docs/components/accordion' },
    { name: 'Lucide', url: 'https://lucide.dev/' },
  ]}
/>

### Installation

```package-install
npx shadcn@latest add @redpanda/accordion
```

<ComponentPreview name="accordion-demo" />

## Usage

### Basic Example

```tsx lineNumbers
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

<ComponentPreview name="accordion-multiple-demo" description="Any number of items may stay open at once" />

```tsx lineNumbers
<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

```tsx lineNumbers
<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

<ComponentPreview name="accordion-contained-simple-demo" description="Contained variant with simple styling" />

The contained variant provides a more visually distinct card-like appearance with rounded borders and background colors:

```tsx lineNumbers
<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

<ComponentPreview
  name="accordion-controlled-demo"
  description="Controlled accordion with external state and programmatic expand/collapse buttons."
/>

### Disabled Item

<ComponentPreview
  name="accordion-disabled-item-demo"
  description="Accordion with a per-item disabled state for gated content."
/>

### Contained with Custom Elements

<ComponentPreview name="accordion-contained-demo" description="Contained variant with icons and badges" />

The contained variant supports custom start and end elements for more complex layouts:

```tsx lineNumbers expandable
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:

```mermaid
graph TD;
    A[❓ Need to display content?] --> B{Content is lengthy or complex?};
    B -->|Yes| C{Users need to see multiple sections at once?};
    B -->|No| D[⚠️ Use standard layout or cards];

    C -->|Yes| E[✅ Use Accordion with type='multiple'];
    C -->|No| F[✅ Use Accordion with type='single'];

    G[❓ Space is limited?] --> H{Content is hierarchical?};
    H -->|Yes| E;
    H -->|No| I[⚠️ Consider Tabs or Collapsible];

    classDef recommended fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#000;
    classDef discouraged fill:#fbbf24,stroke:#f59e0b,stroke-width:2px,color:#000;
    classDef decision fill:#60a5fa,stroke:#3b82f6,stroke-width:2px,color:#fff;
    classDef neutral fill:#9ca3af,stroke:#6b7280,stroke-width:2px,color:#fff;

    class E,F recommended;
    class D,I discouraged;
    class A,G,C,H decision;
    class B decision;
```

**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) => void
```

**Component Hierarchy:**

1. **Accordion** (Root): Manages overall state and behavior (single vs multiple)
2. **AccordionItem**: Individual accordion section with unique value
3. **AccordionHeader**: Contains the clickable trigger (automatically included)
4. **AccordionTrigger**: Clickable element that toggles the content visibility
5. **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

<div className="flex flex-row gap-x-3">
  <ExternalLink href="https://base-ui.com/react/components/accordion" text="Docs" />
  <ExternalLink href="https://base-ui.com/react/components/accordion#api-reference" text="API Reference" />
</div>

## 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 `AccordionTrigger` renders inside an `AccordionHeader` (an `<h3>` by default). Override the tag via `render` on
  `AccordionHeader` if the heading level doesn't match your document outline.
- Triggers expose `aria-expanded`, `aria-controls`, and Base UI's native `data-panel-open` attribute — CSS
  selectors like `data-[panel-open]:rotate-180` keep working.
- In `type="single"` mode the currently-open item can always be collapsed again.

### Component Props

#### Accordion

| Prop | Type | Default | Description |
| - | - | - | - |
| `variant?` | `'simple' \| 'contained'` | `'simple'` | The visual style variant of the accordion |

#### AccordionTrigger

| Prop | Type | Default | Description |
| - | - | - | - |
| `chevron?` | `boolean` | `true` | Whether to show the accordion chevron |
| `start?` | `React.ReactNode` | `undefined` | Optional element to display at the start of the trigger (e.g., icons) |
| `end?` | `React.ReactNode` | `undefined` | Optional element to display at the end of the trigger before the chevron (e.g., badges) |

## Don'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**:

```tsx title="components/redpanda-ui/accordion.tsx"
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>
  );
}
```

## Credits

- We use [Base UI](https://base-ui.com/react/components/accordion) for the accordion component.
- We take our inspiration from [Shadcn UI](https://ui.shadcn.com/docs/components/accordion) for the accordion style.
