A section that people can expand and collapse to show or hide its content.
Collapsible is an open-source component from suiss/ui for React, React Native, SwiftUI and Jetpack Compose. The @esuiss/ui CLI copies its source into your project, so you own the code.
Collapsible wraps the collapsible primitive of the chosen base. The Collapsible root holds the open state, a CollapsibleTrigger toggles it, and CollapsibleContent holds the content that is shown or hidden. The parts are unstyled, so you control layout and appearance with className, or render the trigger as another component such as Button.
Render the trigger as another component with the render prop.
Set asChild on CollapsibleTrigger to render another component as the trigger.
Any Button with slot="trigger" inside Collapsible toggles it.
Use a collapsible for a single disclosure region. For a set of related, stacked sections, use an accordion instead.
Installation
CLI
$ pnpm dlx shadcn@latest add @esuiss/collapsible
Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:
$ pnpm dlx @esuiss/ui@latest add collapsible
Manual
Install the dependencies:
$ pnpm add @base-ui/react
Copy the source into components/ui/collapsible.tsx:
The root is uncontrolled by default; set defaultOpen to start expanded. To control it, pass open and onOpenChange. Set disabled on the root to prevent toggling. While collapsed, the content is removed from the page.
Examples
By default
A collapsible that starts expanded using defaultOpen.
Disabled
A disabled collapsible whose trigger does not toggle the content.
The root that holds the open state and groups the trigger and content.
Also accepts the props of the primitive part it wraps.
02
CollapsibleTrigger
The button that toggles the content open and closed.
Also accepts the props of the primitive part it wraps.
03
CollapsibleContent
The panel that is shown when the collapsible is open.
Also accepts the props of the primitive part it wraps.
Accessibility
The trigger renders as a button and sets aria-expanded to reflect the open state.
The trigger toggles on click, and with Enter or Space when focused.
Give an icon-only trigger an accessible name with aria-label.
Notes
Styling hooks: data-slot="collapsible", data-slot="collapsible-trigger" and data-slot="collapsible-content".
The primitive sets state attributes on the parts, which you can use for styling and animation.
No styles are applied by default.
Collapsible is an unstyled root with a CollapsibleTrigger and a CollapsibleContent panel. It handles the open state and accessibility, and leaves the look to you.
Use it for a single show-more section. For a list of sections where opening one can close another, use Accordion.
Add the pieces it builds on: the primitives components/primitives/collapsible.tsx.
Copy the source into components/ui/collapsible.tsx:
Update the import paths to match your project.
Add the pieces it builds on: the primitives components/primitives/collapsible.tsx.
Copy the source into components/ui/collapsible.tsx:
Update the import paths to match your project.
Add the pieces it builds on: the primitives components/primitives/collapsible.tsx.
Copy the source into components/ui/collapsible.tsx:
Update the import paths to match your project.
Target iOS 17 or later.
Copy the theme files every view uses:
swift/Theme/Theme.swift
import SwiftUIimport UIKitstruct RGBA: Sendable { let red: CGFloat let green: CGFloat let blue: CGFloat let alpha: CGFloat init(_ hex: UInt32, _ alpha: CGFloat = 1) { red = CGFloat((hex >> 16) & 0xFF) / 255 green = CGFloat((hex >> 8) & 0xFF) / 255 blue = CGFloat(hex & 0xFF) / 255 self.alpha = alpha }
swift/Theme/Tokens.swift
import SwiftUIpublic enum EsuissColor { public static let background = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x000000)) public static let foreground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let card = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F)) public static let cardForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let popover = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0x1D1D1F)) public static let popoverForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let primary = Color.adaptive(light: .init(0x0071E3), dark: .init(0x2997FF)) public static let primaryForeground = Color.adaptive(light: .init(0xFFFFFF), dark: .init(0xFFFFFF)) public static let secondary = Color.adaptive(light: .init(0x000000, 0.04), dark: .init(0xFFFFFF, 0.1)) public static let secondaryForeground = Color.adaptive(light: .init(0x1D1D1F), dark: .init(0xF5F5F7)) public static let muted = Color.adaptive(light: .init(0xF5F5F7), dark: .init(0x161617)) public static let mutedForeground = Color.adaptive(light: .init(0x6E6E73), dark: .init(0xA1A1A6)) public static let accent = Color.adaptive(light: .init(0x000000, 0.06), dark: .init(0xFFFFFF, 0.1))
Copy the source:
Add the files to your app target.
Set minSdk to 24 or higher and add the dependencies:
package com.esuiss.ui.themeimport androidx.compose.runtime.Immutableimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.unit.dp@Immutabledata class Colors( val background: Color, val foreground: Color, val card: Color, val cardForeground: Color, val popover: Color, val popoverForeground: Color, val primary: Color, val primaryForeground: Color,
Update the com.esuiss.ui package names to match your project.
Usage
<CollapsibleDemo initialOpen />
Put any content in CollapsibleTrigger, for example a Text and a caret Icon, and use render to render it as a Button. Use open with onOpenChange for controlled state, or defaultOpen for uncontrolled.
Set keepMounted on CollapsibleContent to keep the content mounted while closed.