Introducing PrimeReact v11-alpha 🎉Discover Now

useAccordion

Hooks that manage accordion state, keyboard navigation, and ARIA attributes.

PrimeReact is a rich set of open source UI components for React.
basic-demo

Usage#

import { useAccordion, useAccordionPanel } from '@primereact/headless/accordion';
 
const accordion = useAccordion({ defaultValue: '1' });
const { rootProps } = accordion;
 
const { panelProps, headerProps, triggerProps, contentProps, indicatorProps, state } = useAccordionPanel({
    value: '1',
    context: accordion
});
 
return (
    <div {...rootProps}>
        <div {...panelProps}>
            <div {...headerProps}>
                <button {...triggerProps}>
                    <span {...indicatorProps} />
                </button>
            </div>
            {state.open && <div {...contentProps}></div>}
        </div>
    </div>
);

useAccordion manages root state while useAccordionPanel consumes that context to return prop objects for each panel element — see Primitive for a component-based API.

Features#

  • Two-hook architecture: useAccordion for root state, useAccordionPanel for per-panel behavior
  • Returns spread-ready prop objects (rootProps, panelProps, triggerProps, contentProps, headerProps, indicatorProps)
  • Controlled and uncontrolled value management
  • Single or multiple panel expansion
  • Exposes state, updateValue, and isItemActive for custom logic
  • openOnFocus option for focus-triggered expansion

Behavior#

Default Value#

Use defaultValue to set initially expanded panels.

const accordion = useAccordion({ defaultValue: '1' });

Controlled#

Use value and onValueChange for full programmatic control. The hook never updates its own state in controlled mode.

const [value, setValue] = React.useState<string | null>('1');
const accordion = useAccordion({
    value,
    onValueChange: (e) => setValue(e.value)
});

The onValueChange callback receives { originalEvent, value } where value is the new active panel key (or array in multiple mode).

Multiple#

Set multiple to allow multiple panels to be open simultaneously. In this mode, value/defaultValue accepts an array.

const accordion = useAccordion({ multiple: true, defaultValue: ['1', '2'] });

Disabled#

Set disabled on the root to disable all panels, or pass disabled to individual useAccordionPanel calls.

// Disable all panels
const accordion = useAccordion({ disabled: true });
 
// Disable a specific panel
const panel = useAccordionPanel({ value: '2', disabled: true, context: accordion });

When disabled, triggerProps sets tabIndex to -1, adds aria-disabled, and suppresses onClick/onKeyDown handlers.

Open on Focus#

Set openOnFocus to expand panels when their trigger receives focus via keyboard navigation (Arrow keys, Home, End).

const accordion = useAccordion({ openOnFocus: true });

Using updateValue and isItemActive#

The hook exposes imperative methods for custom logic outside the standard trigger flow.

const accordion = useAccordion({ multiple: true });
 
// Programmatically open a panel
accordion.updateValue(syntheticEvent, '3');
 
// Check if a panel is active
if (accordion.isItemActive('2')) {
    // ...
}

In single mode, updateValue toggles between the key and null. In multiple mode, it adds or removes the key from the array.

Conditional Rendering with state.open#

Unlike the primitive component, the headless hook gives you full control over whether content is rendered. Use state.open to conditionally mount or unmount content.

const { contentProps, state } = useAccordionPanel({ value: '1', context: accordion });
 
{
    state.open && <div {...contentProps}>Panel content — only in DOM when open</div>;
}

Alternatively, always render content and use CSS to show/hide:

<div {...contentProps} style={{ display: state.open ? 'block' : 'none' }}>
    Panel content — always in DOM, toggled with CSS
</div>

For animated enter/leave transitions, use the useMotion hook alongside state.open.

Custom Styling with Data Attributes#

Every prop object includes data-scope="accordion" and a data-part attribute. State-dependent attributes (data-open, data-closed, data-disabled) are added automatically.

[data-scope='accordion'][data-part='trigger'] {
    font-weight: 600;
}
 
[data-scope='accordion'][data-part='content'][data-open] {
    animation: slideDown 200ms ease-out;
}
 
[data-scope='accordion'][data-part='content'][data-closed] {
    animation: slideUp 200ms ease-out;
}
 
[data-scope='accordion'][data-part='panel'][data-disabled] {
    opacity: 0.5;
}

triggerProps also includes data-content-open/data-content-closed for styling the trigger based on content state.

[data-scope='accordion'][data-part='trigger'][data-content-open] {
    color: blue;
    font-weight: 700;
}
 
[data-scope='accordion'][data-part='trigger'][data-content-closed] {
    color: inherit;
    font-weight: 400;
}

API#

useAccordion#

NameTypeDefault
defaultValuestring | number | (string | number)[]—
Default value of the active panel or an array of values in multiple mode.
valuestring | number | (string | number)[]—
Value of the active panel or an array of values in multiple mode.
multiplebooleanfalse
When enabled, multiple tabs can be activated at the same time.
disabledbooleanfalse
When disabled, the component cannot be interacted with.
tabIndexnumber0
Index of the element in tabbing order.
openOnFocusbooleanfalse
When enabled, the accordion panel will be opened on focus.
onValueChange(event: useAccordionChangeEvent) => void—
Callback fired when the accordion's value changes.

useAccordionPanel#

NameTypeDefault
valuestring | number—
Unique value of the panel.
disabledbooleanfalse
Whether the panel is disabled.
contextuseAccordionInstance—
The parent accordion instance providing state and methods.

Accessibility#

See Accordion Primitive for WAI-ARIA compliance details and keyboard support.