rapidUI

Collapsible

Show and hide a single region of content, built on Base UI Collapsible.

Installation

pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/collapsible.json

Installs to @/components/ui/collapsible.

Manual installation

Install the dependencies:

pnpm add @base-ui/react@^1.8.0

Copy the source into your project:

components/ui/collapsible.tsx
'use client'

import { Collapsible as CollapsiblePrimitive } from '@base-ui/react/collapsible'

function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
	return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
}

function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
	return <CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
}

function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
	return <CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
}

export { Collapsible, CollapsibleTrigger, CollapsibleContent }

An interactive component which expands and collapses a panel. Built on top of Base UI Collapsible, it follows WAI-ARIA design patterns.

The components are unstyled wrappers around the Base UI primitives that add data-slot attributes (collapsible, collapsible-trigger, collapsible-content). Style the trigger yourself, usually by passing a Button to the render prop.

Features

  • Controlled (open / onOpenChange) and uncontrolled (defaultOpen) modes
  • Disabled state
  • render prop on the trigger for composing with Button or any other element
  • data-panel-open on the trigger and data-open / data-closed on the content for styling, for example rotating a chevron
  • Nested collapsibles support
  • Keyboard navigation and screen reader support

Usage

import { ChevronsUpDownIcon } from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from '@/components/ui/collapsible'

export function MyCollapsible() {
  return (
    <Collapsible className="flex flex-col gap-2">
      <div className="flex items-center justify-between">
        <h4 className="text-sm font-medium">Case 2026-04812 has 4 attachments</h4>
        <CollapsibleTrigger render={<Button variant="ghost" size="icon-sm" aria-label="Show all attachments" />}>
            <ChevronsUpDownIcon />
          </CollapsibleTrigger>
      </div>
      <CollapsibleContent>
        <p>Site plan, Storgatan 14.pdf</p>
      </CollapsibleContent>
    </Collapsible>
  )
}

// Controlled usage
export function ControlledCollapsible() {
  const [isOpen, setIsOpen] = React.useState(false)

  return (
    <Collapsible open={isOpen} onOpenChange={setIsOpen}>
      <CollapsibleTrigger render={<Button variant="outline" />}>{isOpen ? 'Hide case history' : 'Show case history'}</CollapsibleTrigger>
      <CollapsibleContent>
        <p>Application received via e-service, 2 April 2026</p>
      </CollapsibleContent>
    </Collapsible>
  )
}

Rotating Icon

The trigger gets data-panel-open when expanded, so you can rotate an icon with a group class:

<CollapsibleTrigger render={<Button variant="ghost" className="group" />}>
    Open today 10:00–19:00
    <ChevronDownIcon className="transition-transform group-data-panel-open:rotate-180" />
  </CollapsibleTrigger>

Examples

Default

A list of case attachments where only the first is shown until the icon button is pressed.

With Icon

A full-width trigger with a leading icon and a rotating chevron, revealing a library's opening hours.

Controlled Example

The case history in a Card, controlled with open and onOpenChange so the trigger label can follow the state.

Disabled

A disabled collapsible cannot be toggled. Say why in the trigger.

Nested Collapsibles

Nested collapsibles used to build an archive tree.

Props

Collapsible

  • open - The controlled open state
  • defaultOpen - The default open state
  • onOpenChange - Event handler for open state changes, (open, eventDetails) => void
  • disabled - Whether the collapsible is disabled

CollapsibleTrigger

  • render - The element to render instead of a button, e.g. render={<Button variant="outline" />}
  • children - Content to display in the trigger
  • className - Additional CSS classes

The trigger renders no icon of its own; add one yourself.

CollapsibleContent

  • keepMounted - Keep the content mounted when closed
  • hiddenUntilFound - Keep closed content findable with the browser's find-in-page, which opens the panel on a match
  • children - Content to display when expanded
  • className - Additional CSS classes

Accessibility

The Collapsible component follows WAI-ARIA guidelines:

  • Uses proper ARIA roles (button, region)
  • Supports keyboard navigation:
    • Space/Enter: Toggle expansion
    • Tab: Navigate to trigger
  • Announces state changes to screen readers
  • Maintains focus management
  • Provides visual feedback for interaction states

Best Practices

  1. Content Organization:

    • Keep content concise and focused
    • Use clear trigger text
    • Consider content hierarchy
    • Group related information
  2. Visual Design:

    • Maintain consistent spacing
    • Use clear visual indicators
    • Provide feedback for state changes
    • Consider mobile touch targets
  3. Interaction:

    • Keep animations smooth
    • Handle focus properly
    • Consider loading states
    • Manage nested collapsibles
  4. Implementation:

    • Use semantic markup
    • Handle state changes gracefully
    • Consider performance
    • Test edge cases
  • Accordion
  • Drawer