QUI React

Accordion

Accordions enable multiple content sections to be displayed conditionally in a limited space.

import {QAccordion} from "@qui/react"

Accessibility

Examples

Expanded State

By default, only one panel can be expanded at a time. To change this behavior, set multiple to true.

Default
Multiple
import {ReactNode} from "react"
import {
QAccordion,
QAccordionButton,
QAccordionItem,
QAccordionPanel,
} from "@qui/react"
export default function Showcase(): ReactNode {
return (
<div className="grid grid-cols-1 justify-center gap-8 sm:grid-cols-2">
<div className="flex max-w-[280px] flex-col gap-4">
<span className="text-primary q-font-heading-xs">Default</span>
<QAccordion className="w-[280px]">
<QAccordionItem>
<QAccordionButton>Panel 1</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton>Panel 2</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton>Panel 3</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
</QAccordion>
</div>
<div className="flex max-w-[280px] flex-col gap-4">
<span className="text-primary q-font-heading-xs">Multiple</span>
<QAccordion className="w-[280px]" multiple>
<QAccordionItem>
<QAccordionButton>Panel 1</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton>Panel 2</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton>Panel 3</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
</QAccordion>
</div>
</div>
)
}

Sizes

Customize size using the size property.

import {ReactNode} from "react"
import {Box} from "lucide-react"
import {QAccordionItemSize} from "@qui/base"
import {
QAccordion,
QAccordionButton,
QAccordionItem,
QAccordionPanel,
} from "@qui/react"
const sizes: QAccordionItemSize[] = ["s", "m", "l"]
export default function Sizes(): ReactNode {
return (
<div className="flex flex-wrap justify-center gap-4">
{sizes.map((size) => {
return (
<QAccordion
key={size}
// define a max width so that the collapsed and expanded accordion are
// always the same width.
className="w-[280px]"
size={size}
>
<QAccordionItem>
<QAccordionButton startIcon={Box}>Size {size}</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
</QAccordion>
)
})}
</div>
)
}

Customization

Each item's button and content are customizable.

import {ReactNode} from "react"
import {AlertTriangle} from "lucide-react"
import {
QAccordion,
QAccordionButton,
QAccordionItem,
QAccordionPanel,
QNotification,
} from "@qui/react"
export default function ContentCustomization(): ReactNode {
return (
<div className="flex flex-wrap justify-center gap-4">
<QAccordion className="w-[350px]">
<QAccordionItem>
<QAccordionButton>Default Button</QAccordionButton>
<QAccordionPanel>Default content style</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton startIcon={AlertTriangle}>
Customized Button
</QAccordionButton>
<QAccordionPanel>
<QNotification label="Custom Content" />
</QAccordionPanel>
</QAccordionItem>
</QAccordion>
</div>
)
}

Controlled Accordion

The component can be controlled or uncontrolled.

When you supply the onExpandedChange and expanded props, the component is controlled. Otherwise, the component is uncontrolled and the value is tracked internally.

Learn more about controlled and uncontrolled components in the related React Documentation.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Default Active State

Use the defaultExpanded input to configure the accordion to open a specific index (or indices) on initialization.

Note: this will only impact the accordion when it's first initialized. For fully controlled behavior, refer to the controlled example.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
import {ReactNode} from "react"
import {
QAccordion,
QAccordionButton,
QAccordionItem,
QAccordionPanel,
} from "@qui/react"
export default function DefaultActiveState(): ReactNode {
return (
<div className="flex flex-wrap justify-center gap-4">
<QAccordion className="w-[280px]" defaultExpanded={0}>
<QAccordionItem>
<QAccordionButton>Panel 1</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton>Panel 2</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
<QAccordionItem>
<QAccordionButton>Panel 3</QAccordionButton>
<QAccordionPanel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</QAccordionPanel>
</QAccordionItem>
</QAccordion>
</div>
)
}

Multiple Type Parameter

The value type of the expanded and defaultExpanded props are inferred from the value of the multiple prop. This is accomplished using TypeScript generics. Here's what you need to know: when multiple is true, these props are assumed to be of type number[]. Otherwise, these values default to the non-array number type. This also applies to the value parameter of the onExpandedChange callback.

Consider the following example:

export function Example(): ReactNode {
const [index, setIndex] = useState<number>(0)
return (
<>
{/*
* since `multiple` is not true, TypeScript knows that the expanded
* value is of type number.
*/}
<QAccordion expanded={index} onExpandedChange={setIndex}></QAccordion>
{/*
* This will emit an error because index is of type `number`, but the
* accordion is expecting `number[]` since `multiple` is true.
*/}
<QAccordion
expanded={index}
multiple
onExpandedChange={setIndex}
></QAccordion>
</>
)
}

API

QAccordionProps

Note that the QAccordionProps are generic and include the following type parameters:

  • C: inferred from the as prop. This is the React ElementType or ComponentType of the component's root element.
  • Multiple: inferred from the multiple prop.
Name & DescriptionOption(s)Default
If true, an animation will play when an item is expanded or collapsed.
boolean
true
The component used for the root node. It can be a React component or element.
| ElementType
| ComponentType
'div'
The className property of the Element interface gets and sets the value of the class attribute of the specified element.
string
Default visible indices.
| number
| Array<number>
Used to manually control accordion item visibility.
| number
| Array<number>
Modify the placement of the expand icon
| 'start'
| 'end'
'end'
Allow more than one panel to be open at a time. Note that the value of this prop determines the value of the Multiple type parameter.
boolean
Callback fired whenever an item is expanded or collapsed.
  • expandedItemsThe updated state. Will be an array if multiple is true, otherwise this will be a single value.
  • eventThe DOM event that triggered this change, typically a click or keypress.
  • itemIndexThe index of the selected item.
  • expandedWhether the item is expanded as a result of this change.
    (
    expandedItems: Multiple extends true
    ? number[]
    : number,
    event: SyntheticEvent,
    itemIndex: number,
    expanded: boolean,
    ) => void
    Size of the accordion item and its contents.
    | 's'
    | 'm'
    | 'l'
    The style global attribute contains CSS styling declarations to be applied to the element.
    CSSProperties
    Type
    boolean
    Default
    true
    
    Description
    If true, an animation will play when an item is expanded or collapsed.
    Type
    | ElementType
    | ComponentType
    Default
    'div'
    
    Description
    The component used for the root node. It can be a React component or element.
    Type
    string
    Description
    The className property of the Element interface gets and sets the value of the class attribute of the specified element.
    Type
    | number
    | Array<number>
    Description
    Default visible indices.
    Type
    | number
    | Array<number>
    Description
    Used to manually control accordion item visibility.
    Type
    | 'start'
    | 'end'
    Default
    'end'
    
    Description
    Modify the placement of the expand icon
    Type
    boolean
    Description
    Allow more than one panel to be open at a time. Note that the value of this prop determines the value of the Multiple type parameter.
    Type
    (
    expandedItems: Multiple extends true
    ? number[]
    : number,
    event: SyntheticEvent,
    itemIndex: number,
    expanded: boolean,
    ) => void
    Description
    Callback fired whenever an item is expanded or collapsed.
    • expandedItemsThe updated state. Will be an array if multiple is true, otherwise this will be a single value.
    • eventThe DOM event that triggered this change, typically a click or keypress.
    • itemIndexThe index of the selected item.
    • expandedWhether the item is expanded as a result of this change.
      Type
      | 's'
      | 'm'
      | 'l'
      Description
      Size of the accordion item and its contents.
      Description
      The style global attribute contains CSS styling declarations to be applied to the element.

      QAccordionItemProps

      Name & DescriptionOption(s)Default
      The component used for the root node. It can be a React component or element.
      | ElementType
      | ComponentType
      'div'
      
      The className property of the Element interface gets and sets the value of the class attribute of the specified element.
      string
      If true, the accordion item will be disabled.
      boolean
      false
      
      If true, the divider after the accordion item will be hidden.
      boolean
      false
      
      The style global attribute contains CSS styling declarations to be applied to the element.
      CSSProperties
      Type
      | ElementType
      | ComponentType
      Default
      'div'
      
      Description
      The component used for the root node. It can be a React component or element.
      Type
      string
      Description
      The className property of the Element interface gets and sets the value of the class attribute of the specified element.
      Type
      boolean
      Default
      false
      
      Description
      If true, the accordion item will be disabled.
      Type
      boolean
      Default
      false
      
      Description
      If true, the divider after the accordion item will be hidden.
      Description
      The style global attribute contains CSS styling declarations to be applied to the element.

      Keyboard Events

      If an accordion item's button is focused:
      • Moves focus to the next item's button. If focus is on the last accordion item's button, moves focus to the first accordion item's button.

      • Moves focus to the previous item's button. If focus is on the first accordion item's button, moves focus to the last accordion item's button.

      • Toggles the visibility of the panel: if the item's panel is closed, it is opened. If the item's panel is open, it is closed.