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/onOpenChangeto drive externally or rely ondefaultOpen - Imperative controls —
toggle(),open(), andclose()let you flip state from anywhere outside the trigger - Full render control —
state.opendecides 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#
| Name | Type | Default |
|---|---|---|
open | boolean | false |
| Controls the open state of the collapsible. | ||
defaultOpen | boolean | false |
| Defines the initial open state of the collapsible. | ||
disabled | boolean | false |
| When disabled, the component cannot be interacted with. | ||
tabIndex | number | 0 |
| 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.