Collapsible
Primitive

Collapsible

Headless disclosure primitives for showing and hiding one section of content.

Live behavior

Collapsible in motion

Interactive
Atom behavior · App-owned appearance
Preparing behavior…

Open and close the region without moving its trigger.

waiting for input

When to Use

Use Collapsible when one control reveals one related block, such as advanced settings, extra details, or a filter panel. Use Accordion when several named sections belong together and users move between them. Use Dialog when the content must interrupt the page in a separate modal layer.

Features

The behavior Atom owns before your product adds appearance.

  • Supports controlled and uncontrolled open state.
  • Supports vertical and horizontal expansion intent.
  • Connects Trigger and Content with generated ARIA IDs.
  • Supports disabled triggers and custom trigger rendering.
  • Keeps Content mounted on request for exit animations.
  • Exposes open state and measured content size for consumer-owned animation.

Import

tsx
import { Collapsible } from "@flowstack-ui/atom";

Anatomy

tsx
<Collapsible.Root>
  <Collapsible.Trigger />
  <Collapsible.Content />
</Collapsible.Root>

API Reference

Root

Owns the disclosure state and shares it with Trigger and Content. It renders a div by default and accepts native div props.

PropTypeDefault
openboolean-
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void-
disabledbooleanfalse
orientation"vertical" | "horizontal""vertical"
asChildbooleanfalse
renderRenderProp-
Data attributeValues
[data-slot]"collapsible"
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-orientation]"vertical" | "horizontal"

Trigger

Toggles Content. It renders a native button by default and preserves button keyboard behavior; custom elements receive button semantics.

PropTypeDefault
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
role"button" for a custom rendered element
aria-expandedCurrent open state
aria-controlsGenerated Content ID
aria-disabled"true" when Root is disabled
Data attributeValues
[data-slot]"collapsible-trigger"
[data-state]"open" | "closed"
[data-disabled]Present when disabled
[data-orientation]"vertical" | "horizontal"

Content

Contains the disclosed region and identifies Trigger as its accessible label. It unmounts after closing by default, while keepMounted leaves a hidden copy available for consumer-owned exit animation.

PropTypeDefault
keepMountedbooleanfalse
ARIA attributeValues
role"region"
aria-labelledbyGenerated Trigger ID
Data attributeValues
[data-slot]"collapsible-content"
[data-state]"open" | "closed"
[data-initial-open]Present while initially open Content has not transitioned
[data-orientation]"vertical" | "horizontal"

Content sets --content-height and --content-width to its measured natural size for optional consumer-owned animation. Both stay synchronized while mounted when responsive reflow, fonts, images, or other intrinsic changes alter the panel. orientation is behavior metadata for styled layers: vertical motion uses height and horizontal motion uses width. Trigger keyboard activation does not change.

Examples

Basic Disclosure

tsx
import { Collapsible } from "@flowstack-ui/atom";

export function AdvancedSettings() {
  return (
    <Collapsible.Root>
      <Collapsible.Trigger>Advanced settings</Collapsible.Trigger>
      <Collapsible.Content>
        These settings are only needed for custom configurations.
      </Collapsible.Content>
    </Collapsible.Root>
  );
}

Controlled State

tsx
import { useState } from "react";
import { Collapsible } from "@flowstack-ui/atom";

export function ControlledDetails() {
  const [open, setOpen] = useState(false);

  return (
    <Collapsible.Root open={open} onOpenChange={setOpen}>
      <Collapsible.Trigger>
        {open ? "Hide details" : "Show details"}
      </Collapsible.Trigger>
      <Collapsible.Content>Additional account information.</Collapsible.Content>
    </Collapsible.Root>
  );
}

Accessibility

Collapsible follows the WAI-ARIA Disclosure pattern. Trigger exposes whether Content is open and points to it with aria-controls. Content is a named region. Give Trigger clear text that tells the user what will be revealed.

KeyDescription
EnterToggles Content while the native or custom button has focus.
SpaceToggles Content while the native or custom button has focus.

Changelog

Unreleased

0.24.0

  • Added source-led Agent Knowledge for single-disclosure selection, trigger and region relationships, conditional persistence, orientation metadata, and motion measurement boundaries.

0.20.9

  • Expose initially open Content with data-initial-open until its first state transition so styled layers can suppress page-load entrance motion.

0.20.8

  • Measure newly opened Content before its first painted animation frame so height and width transitions begin with stable intrinsic dimensions.

0.14.0

  • Add orientation="vertical" | "horizontal" with vertical default and consistent Root, Trigger, and Content attributes.
  • Publish live --content-width alongside --content-height for two-axis styled disclosure motion.

0.13.1

  • Keep --content-height synchronized while mounted when responsive reflow, fonts, images, or other intrinsic resizing changes the panel height.

0.1.0

  • Initial Atom release.