Collapsible

A panel that expands and collapses, with a height animation.

3 files changed

src/app/page.tsx
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible'
import { Button } from '@/components/ui/button'

export default function CollapsibleDemo() {
  return (
    <Collapsible className="w-full max-w-sm space-y-2">
      <div className="flex items-center justify-between gap-4">
        <h4 className="text-sm font-semibold">3 files changed</h4>
        <CollapsibleTrigger
          render={
            <Button variant="ghost" size="sm" className="group" aria-label="Toggle file list">
              <ChevronsIcon className="transition-transform group-data-[panel-open]:rotate-180" />
            </Button>
          }
        />
      </div>
      <div className="rounded-md border border-border px-4 py-2 font-mono text-sm">src/app/page.tsx</div>
      <CollapsibleContent>
        <div className="space-y-2">
          <div className="rounded-md border border-border px-4 py-2 font-mono text-sm">src/components/header.tsx</div>
          <div className="rounded-md border border-border px-4 py-2 font-mono text-sm">src/lib/utils.ts</div>
        </div>
      </CollapsibleContent>
    </Collapsible>
  )
}

function ChevronsIcon({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
      <path d="m7 15 5 5 5-5M7 9l5-5 5 5" />
    </svg>
  )
}

Built on Base UI

Installation

pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/collapsible.json

Needs a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.

Collapsible imports cn() from lib/utils.ts — add it too if you don't have it yet:

import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Usage

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

<Collapsible>
  <CollapsibleTrigger render={<Button variant="outline">Toggle</Button>} />
  <CollapsibleContent>Hidden until the trigger is pressed.</CollapsibleContent>
</Collapsible>

Examples

Controlled

'use client'

import * as React from 'react'
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/collapsible'
import { Button } from '@/components/ui/button'

export default function CollapsibleControlled() {
  const [open, setOpen] = React.useState(false)

  return (
    <Collapsible open={open} onOpenChange={setOpen} className="w-full max-w-sm">
      <CollapsibleTrigger render={<Button variant="outline">{open ? 'Hide details' : 'Show details'}</Button>} />
      <CollapsibleContent>
        <p className="pt-3 text-sm text-muted-foreground">
          Details are kept out of the way until they&apos;re asked for. The panel animates to its natural height, and its
          content stays out of the tab order while it&apos;s closed.
        </p>
      </CollapsibleContent>
    </Collapsible>
  )
}

API Reference

NameTypeDefaultDescription
open / onOpenChangeboolean / (open: boolean) => void—Control the open state from outside (set on Collapsible).
defaultOpenbooleanfalseInitial open state (uncontrolled).
disabledbooleanfalseIgnores user interaction.
CollapsibleTrigger renderReactElement—Renders the trigger as a different element, e.g. a Button. It gets data-panel-open while open.
CollapsibleContent keepMountedbooleanfalseKeeps the content in the DOM while closed.
CollapsibleContent hiddenUntilFoundbooleanfalseLets the browser's find-in-page open the panel when it matches.