ComponentsLayout

Collapsible

A trigger that shows and hides one region in place. Use it for details, longer lists, and settings that most people do not need every time.

Open by default

Show or hide sample project details. Nothing is loaded or saved.

The sample Meadow project has three tasks and two team members.

Closed by default

Show or hide sample project details. Nothing is loaded or saved.

Usage

Collapsible usageTSX
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@tulipfarm/sprout/collapsible";
import { Button } from "@tulipfarm/sprout/button";

<Collapsible>
  <CollapsibleTrigger render={<Button variant="ghost" size="sm" />}>
    Approval details
  </CollapsibleTrigger>
  <CollapsibleContent>
    <p>Only the listed changes are approved.</p>
  </CollapsibleContent>
</Collapsible>

Collapsible holds the open state. Pass defaultOpen to let it manage itself, or open and onOpenChange to control it. disabled blocks the trigger.

Triggers

The trigger is unstyled. Give it a Sprõut Button with render. Use outline for a main section, ghost for secondary details, xs and link inside dense status rows, andchat-row for full-width rows inside a list.

Trigger pairings

Each trigger renders a Sprõut Button. Variant and size follow how much weight the hidden content has.

outline · default

Retries: 3 attempts, 30 seconds apart.

Time zone: Europe/Amsterdam. Runs pause on public holidays.

ghost · default

2 Drive folders and 1 shared drive are searchable in chat.

ghost · sm

Only the listed changes are approved. Each table applies as one unit. Completed tables stay applied if later work stops.

ghost · xs
  • Every invoice has an owner: passed call_7f2a91c4e0b8
  • Totals match the ledger: passed call_3b19d0aa6f52
link · sm
ghost · chat-row

Read and inspect
Create or change data

Content variants

Content variantsTSX
<CollapsibleContent>…</CollapsibleContent>               // instant
<CollapsibleContent variant="sidebar">…</CollapsibleContent> // height and fade
<CollapsibleContent variant="card">…</CollapsibleContent>    // height only

Content variants

CollapsibleContent variant sets how the panel enters and leaves. Reduced motion turns every transition off.

default

Shows and hides at once, for short notes under a control.

  • Only the listed changes are approved.
sidebar

Grows and fades in 180ms, for nested navigation.

card

Grows in 180ms inside a bordered card.

  • Invoices: 6 fields
  • Vendors: 4 fields

Indicators

Turning chevronsTSX
<CollapsibleTrigger>
  <Sparkle aria-hidden="true" />
  Thought for 12s
  <ChevronDown aria-hidden="true" data-slot="collapsible-indicator" />
</CollapsibleTrigger>

<ChevronRight
  aria-hidden="true"
  data-slot="collapsible-indicator"
  data-direction="right"
/>

Mark a chevron with data-slot="collapsible-indicator" and it turns 180° while open. Add data-direction="right" for a right chevron that turns 90°. It works inside the trigger, and anywhere in the header outside the content, so a chevron can sit beside a badge. To swap the icon or label instead, read the state you control.

Reasoning and sources

Plain triggers with an icon, a label, and a turning chevron. Text stays muted until hover.

Thinking
Complete
The user wants overdue invoices. I will filter the Invoices table by due date before today and group the results by vendor.
Interrupted
Sources

Activity

The root is a labelled region. Content stays mounted, so the scroll position survives a close.

Working
  1. Searched Drive for “Q3 planning”
  2. Read Q3 planning notes
  3. Read Vendor contract — Northwind
Worked
  1. Searched Drive for “Q3 planning”
  2. Read Q3 planning notes
  3. Read Vendor contract — Northwind
  4. Queried Invoices table
  5. Summarized 12 overdue invoices

Tool calls

A full-width header trigger carries the name, a status badge, and a summary that wraps.

Queued
Running
Needs approval

Input

{ "filter": "due<2026-10-18;status!=paid" }
Complete
Failed

The Invoices table took too long to answer.

Cards

Put the trigger in the heading and stretch its hit area over the header with a pseudo-element. Control open to close the card when the review settles.

Review card

The whole header is the hit area. The chevron sits outside the trigger and still turns. Settling the review closes the card.

Needs review
  • Invoices: number, vendor, amount, due date, status, owner
  • Vendors: name, contact, payment terms, country

Sidebar

Render a SidebarMenuAction as the trigger and use the sidebar content variant. Close the list while the sidebar shows only icons.

Sidebar recent itemsTSX
const { isMobile, state } = useSidebar();
const visible = open && (isMobile || state === "expanded");

<Collapsible open={visible} onOpenChange={setOpen}>
  <SidebarMenuButton …>Chats</SidebarMenuButton>
  <CollapsibleTrigger
    aria-label={`${visible ? "Collapse" : "Expand"} Chats`}
    render={<SidebarMenuAction />}
  >
    <ChevronRight data-slot="collapsible-indicator" data-direction="right" />
  </CollapsibleTrigger>
  <CollapsibleContent variant="sidebar">
    <SidebarMenuSub aria-label="Recent chats">…</SidebarMenuSub>
  </CollapsibleContent>
</Collapsible>

Sidebar recent items

A SidebarMenuAction is the trigger. The sidebar content variant grows the nested list.

Collapse the sidebar to icons and the list closes. Expand it and the list returns.

States

Content can sit before the trigger so a list grows from its button. keepMounted keeps closed content in the page, so form state and scroll position survive. Pass open with no trigger to show a fixed list.

Disabled

A disabled root blocks the trigger. Say why next to it.

The details cannot be opened in this disabled sample.

Content above the trigger

Put the content first and the list grows upward from the button. The label and icon follow the open state.

Start with a task

  • Summarize unread email
  • Plan my week

Mount on first open

keepMounted keeps the panel in the page. Render the children only after the first open to defer their work.

Always open

Pass open with no trigger to show the content as a fixed list, with loading and error rows inside.

Loading earlier activity…
  1. Searched Drive for “Q3 planning”
  2. Read Q3 planning notes
  3. Read Vendor contract — Northwind

API

The three parts wrap Base UI Collapsible. Every Base UI prop works, including render, keepMounted, hiddenUntilFound, and className as a string or a state function.

Parts get data-slot values of collapsible, collapsible-trigger, and collapsible-content. Pass your own data-slot to rename a root. The root has data-open or data-closed, the trigger has data-panel-open, and the content has data-variant plus Base UI's starting and ending style attributes. collapsibleContentVariantNames lists the variants.

Accessibility

The trigger is a button with aria-expanded and aria-controls. Enter and Space toggle it. Give icon-only triggers an aria-label that names the action, and link a disabled trigger to its reason with aria-describedby. Put the trigger inside a heading when it names a section.

Chevrons turn over 120ms and animated content grows over 180ms. Both stop when reduced motion is requested.

Theme tokens

Collapsible uses --sprout-focus, --sprout-radius-control, --sprout-motion-fast, --sprout-motion-normal, and --sprout-motion-ease. Everything else comes from the trigger and content you render, so it follows light, dark, and the TulipFarm theme.