Headless collapsible (show/hide) component for vanilla JavaScript. Accessible, unstyled, tiny.
npm install @data-slot/collapsibleHeadless collapsible (show/hide) component for vanilla JavaScript. Accessible, unstyled, tiny.
``bash`
npm install @data-slot/collapsible
`html
Hidden content revealed on click.
`
Auto-discover and bind all collapsible instances in a scope (defaults to document).
`typescript
import { create } from "@data-slot/collapsible";
const controllers = create(); // Returns CollapsibleController[]
`
Create a controller for a specific element.
`typescript
import { createCollapsible } from "@data-slot/collapsible";
const collapsible = createCollapsible(element, {
defaultOpen: false,
onOpenChange: (open) => console.log(open),
});
`
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| defaultOpen | boolean | false | Initial open state |onOpenChange
| | (open: boolean) => void | undefined | Callback when open state changes (not called on init) |
Options can also be set via data attributes on the root element. JS options take precedence.
| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| data-default-open | boolean | false | Initial open state |
Boolean attributes: present or "true" = true, "false" = false, absent = default.
`html`
...
| Method/Property | Description |
|-----------------|-------------|
| open() | Open the collapsible |close()
| | Close the collapsible |toggle()
| | Toggle the collapsible |isOpen
| | Current open state (readonly boolean) |destroy()
| | Cleanup all event listeners |
`html`
Content
Both collapsible-trigger and collapsible-content are required.
Use data-state attributes for CSS styling (available on both root and content):
`css
/ Hidden state /
[data-slot="collapsible-content"][hidden] {
display: none;
}
/ Or use data-state /
[data-slot="collapsible"][data-state="closed"] [data-slot="collapsible-content"] {
display: none;
}
/ Animate using data-state on content /
[data-slot="collapsible-content"] {
overflow: hidden;
transition: max-height 0.3s;
max-height: 0;
}
[data-slot="collapsible-content"][data-state="open"] {
max-height: 500px;
}
`
With Tailwind:
`html`
Content here
The component automatically handles:
- aria-expanded state on triggeraria-controls
- linking trigger to contentrole="region"
- on contentaria-labelledby
- linking content back to triggerdisabled
- Unique ID generation for trigger and content
- Disabled trigger support (respects attribute and aria-disabled="true")
Listen for changes via custom events:
`javascript`
element.addEventListener("collapsible:change", (e) => {
console.log("Collapsible open:", e.detail.open);
});
Control the collapsible via events:
| Event | Detail | Description |
|-------|--------|-------------|
| collapsible:set | { open: boolean } | Set open state programmatically |
`javascript
// Open the collapsible
element.dispatchEvent(
new CustomEvent("collapsible:set", { detail: { open: true } })
);
// Close the collapsible
element.dispatchEvent(
new CustomEvent("collapsible:set", { detail: { open: false } })
);
`
Note: Blocked when trigger is disabled (has disabled attribute or aria-disabled="true").
#### Deprecated Shapes
The following shape is deprecated and will be removed in v1.0:
`javascript`
// Deprecated: { value: boolean }
element.dispatchEvent(
new CustomEvent("collapsible:set", { detail: { value: true } })
);
Use { open: boolean }` instead.
MIT