Askr documentation
UI & Components

Accordion and Collapsible

Accordion and Collapsible: anatomy, keyboard behavior, state, and theming in Askr.

Example

import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Collapsible, CollapsibleContent, CollapsibleTrigger } from '@askrjs/themes/components';

<>
  <Accordion type="single" collapsible>
    <AccordionItem value="billing">
      <AccordionTrigger>How billing works</AccordionTrigger>
      <AccordionContent>Usage is calculated per workspace.</AccordionContent>
    </AccordionItem>
  </Accordion>
  <Collapsible open={detailsOpen()} onOpenChange={setDetailsOpen}>
    <CollapsibleTrigger>Project details</CollapsibleTrigger>
    <CollapsibleContent><ProjectDetails /></CollapsibleContent>
  </Collapsible>
</>
Try Accordion

Expand one panel at a time and inspect its state.

Published props

Generated from the TypeScript declarations shipped by the installed package. Named types in the Type column define the accepted values.

AccordionContentAsChildProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild: true;——
childrenchildren: JSXElement;——
forceMountforceMount?: boolean | undefined;——
refref?: Ref<Element>;——

AccordionContentOwnProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
forceMountforceMount?: boolean | undefined;——

AccordionContentProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;——
forceMountforceMount?: boolean | undefined;——
refref?: Ref<HTMLDivElement>;——

AccordionHeaderAsChildProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild: true;——
childrenchildren: JSXElement;——
refref?: Ref<Element>;——

AccordionHeaderProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;——
refref?: Ref<HTMLHeadingElement>;——

AccordionItemOwnProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
childrenchildren?: unknown;——
disableddisabled?: boolean | undefined;——
valuevalue: string;——

AccordionItemProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;——
disableddisabled?: boolean | undefined;——
refref?: Ref<HTMLDivElement>;——
valuevalue: string;——

AccordionMultipleProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;—Supports literal, nested, array-mapped, and `For`-rendered descendants.
collapsiblecollapsible?: boolean | undefined;——
defaultValuedefaultValue?: string[] | undefined;——
idid?: string | undefined;——
looploop?: boolean | undefined;——
onValueChangeonValueChange?: ((value: string[]) => void) | undefined;——
orientationorientation?: AccordionOrientation | undefined;——
refref?: Ref<HTMLDivElement>;——
typetype: "multiple";——
valuevalue?: string[] | undefined;——

AccordionProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;—Supports literal, nested, array-mapped, and `For`-rendered descendants.
collapsiblecollapsible?: boolean | undefined;——
defaultValuedefaultValue?: string | string[] | undefined;——
idid?: string | undefined;——
looploop?: boolean | undefined;——
onValueChangeonValueChange?: ((value: string[]) => void) | ((value: string) => void) | undefined;——
orientationorientation?: AccordionOrientation | undefined;——
refref?: Ref<HTMLDivElement>;——
typetype?: "single" | "multiple" | undefined;——
valuevalue?: string | string[] | undefined;——

AccordionSingleProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;—Supports literal, nested, array-mapped, and `For`-rendered descendants.
collapsiblecollapsible?: boolean | undefined;——
defaultValuedefaultValue?: string | undefined;——
idid?: string | undefined;——
looploop?: boolean | undefined;——
onValueChangeonValueChange?: ((value: string) => void) | undefined;——
orientationorientation?: AccordionOrientation | undefined;——
refref?: Ref<HTMLDivElement>;——
typetype?: "single" | undefined;——
valuevalue?: string | undefined;——

AccordionTriggerAsChildProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild: true;——
childrenchildren: JSXElement;——
disableddisabled?: boolean | undefined;——
onPressonPress?: ((event: PressEvent) => void) | undefined;——
refref?: Ref<Element>;——
typetype?: undefined;——

AccordionTriggerProps

Import from @askrjs/themes/accordion.

PropTypeDefaultDescription
asChildasChild?: false | undefined;——
childrenchildren?: unknown;——
disableddisabled?: boolean | undefined;——
onPressonPress?: ((event: PressEvent) => void) | undefined;——
refref?: Ref<HTMLButtonElement>;——
typetype?: "button" | "submit" | "reset" | undefined;——

CollapsibleContentAsChildProps

Import from @askrjs/themes/collapsible.

PropTypeDefaultDescription
asChildasChild: true;—Render as child element instead of div
childrenchildren: JSXElement;—Child content
forceMountforceMount?: boolean | undefined;—Force mount even when closed (for animation)
refref?: Ref<Element>;—Ref forwarding

CollapsibleContentProps

Import from @askrjs/themes/collapsible.

PropTypeDefaultDescription
asChildasChild?: false | undefined;—Render as child element instead of div
childrenchildren?: unknown;—Child content
forceMountforceMount?: boolean | undefined;—Force mount even when closed (for animation)
refref?: Ref<HTMLDivElement>;—Ref forwarding

CollapsibleProps

Import from @askrjs/themes/collapsible.

PropTypeDefaultDescription
childrenchildren?: unknown;—Child components (Trigger, Content)
defaultOpendefaultOpen?: boolean | undefined;—Uncontrolled default open state
disableddisabled?: boolean | undefined;—Whether the collapsible is disabled
idid?: string | undefined;—Stable caller-provided identity used for ARIA linking when available
onOpenChangeonOpenChange?: ((open: boolean) => void) | undefined;—Callback when open state changes
openopen?: boolean | undefined;—Controlled open state

CollapsibleTriggerAsChildProps

Import from @askrjs/themes/collapsible.

PropTypeDefaultDescription
asChildasChild: true;—Render as child element instead of button
childrenchildren: JSXElement;—Child content
refref?: Ref<Element>;—Ref forwarding

CollapsibleTriggerProps

Import from @askrjs/themes/collapsible.

PropTypeDefaultDescription
asChildasChild?: false | undefined;—Render as child element instead of button
childrenchildren?: unknown;—Child content
refref?: Ref<HTMLButtonElement>;—Ref forwarding

Purpose

Accordion groups several related disclosures — think an FAQ list or a settings panel — and manages roving keyboard focus and open state across all of them at once. Collapsible is the single-region primitive Accordion is effectively built from: reach for it when you have one thing to show or hide, and reach for Accordion once you have a list of items sharing that same behavior.

Install and import

Install @askrjs/ui for the headless behavior and @askrjs/themes for the styled defaults. Pull Accordion pieces from @askrjs/ui/accordion (headless) or @askrjs/themes/accordion (styled), and Collapsible pieces from @askrjs/ui/collapsible or @askrjs/themes/collapsible — the two components ship as separate subpaths in both packages, not one combined module.

Live examples

The example composes the themed Accordion and Collapsible directly. Once it is running in your own app, open devtools and watch data-state and aria-expanded change as you toggle an item — those attributes are the styling and testing contract, so confirm your markup produces them before layering custom CSS on top.

Anatomy

Accordion composes an Accordion root around one or more AccordionItem elements, each holding an AccordionHeader/AccordionTrigger pair and an AccordionContent panel; the item's value prop is what open state actually tracks. Collapsible is flatter — just Collapsible, CollapsibleTrigger, and CollapsibleContent — because there's only ever one region, so there's no item wrapper or value to assign.

State model

Accordion runs in one of two modes: type="single" (the default) tracks a single open value string, while type="multiple" tracks a string[]. Both accept value/onValueChange for controlled use or defaultValue for uncontrolled use, plus collapsible (whether the last open item in single mode can close) and loop for keyboard wraparound. Collapsible needs none of that type juggling — it's just open/defaultOpen/onOpenChange as a plain boolean.

Keyboard and accessibility

Triggers expose aria-expanded and aria-controls and support keyboard activation. Accordion additionally coordinates focus among its triggers according to orientation and loop; Collapsible owns a single disclosure. Test the rendered relationships, focus movement and open state through those public components.

Styling and tokens

Both families expose data-slot, data-state, and data-disabled (Accordion adds data-orientation) so CSS can target open, closed, and disabled states without touching JavaScript. Content unmounts when closed by default in both; pass forceMount on AccordionContent or CollapsibleContent if you need the panel present in the DOM to drive a height-based CSS transition.

API

AccordionItem takes a required value plus disabled; AccordionTrigger takes the same button-like props as other interactive primitives, including asChild. Collapsible's root takes open, defaultOpen, onOpenChange, and disabled — there's no type prop because it never tracks more than one boolean, unlike Accordion.

Edge cases

With collapsible={false} (the default for single-type Accordion), at least one item always stays open, so don't build a "collapse all" affordance against that configuration. disabled on an AccordionItem blocks its trigger but leaves the item in the DOM, so a screen reader user still encounters it — if every item ends up disabled, consider hiding the whole accordion instead of leaving a fully inert one on screen.

See Headless versus Themed for how the @askrjs/ui/@askrjs/themes split works in general, and Focus and Dismissal for the keyboard and focus utilities both primitives are built on. The Combobox and Command page covers a different disclosure shape if what you actually need is a filterable list rather than a static one.