Redpanda UIRedpanda UI

Accordion

A vertically stacked set of interactive headings that each reveal an associated section of content.

Made by imskyleen

Installation

Loading component...

Usage

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

Loading component...
<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>

Custom Animation

<Accordion type="single" collapsible>
  <AccordionItem value="animated">
    <AccordionTrigger 
      transition={{ type: 'spring', stiffness: 200, damping: 25 }}
    >
      Custom Animation
    </AccordionTrigger>
    <AccordionContent 
      transition={{ type: 'spring', stiffness: 180, damping: 20 }}
    >
      This accordion uses custom spring animation settings for smoother transitions.
    </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

Loading component...

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, Tailwind CSS, and Motion.
    </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

Loading component...

Disabled Item

Loading component...

Contained with Custom Elements

Loading component...

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-muted-foreground" />}
      end={<Badge variant="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-muted-foreground" />}
      end={<Badge variant="simple">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-muted-foreground" />}
      end={<Badge variant="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 mask animation]    │ │ │
│ └─────────────────────────────────────────────┘ │ │
├─────────────────────────────────────────────────┤ │
│ 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: Motion components provide smooth height and opacity transitions
  • Accessibility: Built on Base UI primitives for keyboard navigation and screen readers
  • Chevron Animation: Icon rotates 180° when content expands using Motion

Props

Keyboard interactions

KeyAction
Tab to triggerFocus moves to the next accordion trigger.
Enter / SpaceToggles the focused item.
ArrowDownMoves focus to the next trigger. Wraps at the end.
ArrowUpMoves focus to the previous trigger. Wraps at the start.
Home / EndJumps 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 the Radix-compat data-state="open" | "closed" attribute — CSS selectors like data-[state=open]:rotate-180 keep working.
  • In type="single" mode Base UI always allows re-closing the open item. Radix's collapsible={false} behavior cannot be faithfully reproduced. See MIGRATION.md.

Animate UI Props

Accordion

Prop

Type

AccordionTrigger

Prop

Type

AccordionContent

Prop

Type

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:

components/redpanda-ui/accordion.tsx
function AccordionContent({
  className,
  children,
  transition = { type: 'spring', stiffness: 150, damping: 22 },
  ...props
}: AccordionContentProps) {
  const { isOpen } = useAccordionItem();

  return (
    <AccordionPrimitive.Content forceMount {...props}>
      <motion.div
        key="accordion-content"
        initial={{ height: 0, opacity: 0, '--mask-stop': '0%' }}
        animate={
          isOpen
            ? { height: 'auto', opacity: 1, '--mask-stop': '100%' }
            : { height: 0, opacity: 0, '--mask-stop': '0%' }
        }
        transition={transition}
        style={{
          maskImage:
            'linear-gradient(black var(--mask-stop), transparent var(--mask-stop))',
          WebkitMaskImage:
            'linear-gradient(black var(--mask-stop), transparent var(--mask-stop))',
        }}
        className="overflow-hidden"
        ref={ref}
      >
        <div className={cn('pb-4 pt-0 text-sm', className)}>{children}</div>
      </motion.div>
    </AccordionPrimitive.Content>
  );
}

Credits

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested b…#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • patchv0.3.1**Accordion** - `AccordionTrigger` now shows `cursor-pointer` on hover.#112
See full history →
Built by malinskibeniamin. The source code is available on GitHub.

On this page