Introducing PrimeReact v11-alpha 🎉Discover Now

usePanel

Hook that manages collapsible panel state and ARIA attributes.

Header
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
basic-demo

Usage#

import { usePanel } from '@primereact/headless/panel';
const { rootProps, triggerProps, contentProps, indicatorProps, state } = usePanel({ defaultOpen: true });
 
<div {...rootProps}>
    <button {...triggerProps}>
        <span {...indicatorProps} />
    </button>
    {state.open && <div {...contentProps}></div>}
</div>;

usePanel wraps useCollapsible and returns spread-ready prop objects for each DOM element — see Primitive for a component-based API.

Features#

  • Single-hook collapsible surface — built on useCollapsible, returns one bundle of props (rootProps, triggerProps, contentProps, indicatorProps) for the whole panel
  • Controlled or uncontrolled open state — pass open/onOpenChange to drive externally or rely on defaultOpen
  • Imperative controls — toggle(), open(), and close() let you flip state from anywhere outside the trigger
  • Full render control — state.open decides whether content is mounted, hidden with CSS, or animated in

Working with callbacks#

Controlled open state#

Drive the panel's open state from parent state with open and onOpenChange.

const [open, setOpen] = React.useState(true);
 
const panel = usePanel({
    open,
    onOpenChange: (e) => setOpen(e.value ?? false)
});

onOpenChange receives { originalEvent, value } where value is the new boolean state.

Imperative open/close from outside the trigger#

The hook exposes toggle, open, and close for buttons that don't live inside the panel — such as a "collapse all" toolbar action.

const panel = usePanel({ defaultOpen: false });
 
<button onClick={(e) => panel.open(e)}>Expand</button>
<button onClick={(e) => panel.close(e)}>Collapse</button>
<button onClick={(e) => panel.toggle(e)}>Toggle</button>

Animating enter/leave#

Because the hook hands you state.open, you can gate rendering on it and wrap content in a motion primitive.

const { contentProps, state } = usePanel({ defaultOpen: true });
 
<AnimatePresence>
    {state.open && (
        <motion.div {...contentProps} initial={{ height: 0 }} animate={{ height: 'auto' }} exit={{ height: 0 }}>
            Panel content
        </motion.div>
    )}
</AnimatePresence>;

Alternatively, keep the content mounted and toggle visibility with CSS:

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

Styling with data attributes#

Every prop object includes data-scope="panel" and a data-part attribute. State is reflected via data-open/data-closed/data-disabled on parts, and data-content-open/data-content-closed on the trigger.

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

API#

usePanel#

NameTypeDefault
openbooleanfalse
Controls the open state of the collapsible.
defaultOpenbooleanfalse
Defines the initial open state of the collapsible.
disabledbooleanfalse
When disabled, the component cannot be interacted with.
tabIndexnumber0
Index of the element in tabbing order.
onOpen(event?: SyntheticEvent) => void—
Callback triggered when the content is opened.
onClose(event?: SyntheticEvent) => void—
Callback triggered when the content is closed.
onOpenChange(event: usePanelOpenChangeEvent) => void—
Callback triggered when the content's toggle state changes.

Accessibility#

Enter or Space on the header toggles the panel when toggleable. See Primitive for full WAI-ARIA compliance details.