Introducing PrimeReact v11-alpha 🎉Discover Now

useDrawer

Hook that manages slide-in panel overlays with focus trap, scroll lock, and dismissable backdrop.

basic-demo

Usage#

import { useMotion } from '@primereact/core/motion';
import { useDrawer } from '@primereact/headless/drawer';
import { usePortal } from '@primereact/headless/portal';
import * as React from 'react';
import { createPortal } from 'react-dom';
const { rootProps, triggerProps, backdropProps, popupProps, closeProps, headerProps, state } = useDrawer();
const portal = usePortal();
 
<div {...rootProps}>
    <button {...triggerProps}></button>
    {portal.state.mounted &&
        createPortal(
            <>
                <div {...backdropProps} />
                <div {...popupProps}>
                    <div {...headerProps}>
                        <button {...closeProps}></button>
                    </div>
                    ...
                </div>
            </>,
            document.body

useDrawer manages open/close state, focus trapping, scroll locking, and dismissable backdrop behavior. See Primitive for a component-based API.

Features#

  • Open/close lifecycle — controlled or uncontrolled visibility with dismissable backdrop and escape handling
  • Focus management — focus trap inside the panel while open, with automatic return-focus to the trigger on close
  • Positioning — slides in from left, right, top, or bottom, exposed as a data-position attribute on the root
  • Scroll and layering — body scroll lock plus baseZIndex/autoZIndex to stack with other overlays
  • Portal-ready — designed to pair with usePortal and createPortal for SSR-safe body portaling
  • Imperative controls — close() method and state.open flag for programmatic control and conditional rendering

Working with callbacks#

Controlled open state#

Pass open and onOpenChange to drive visibility from external state.

const [isOpen, setIsOpen] = React.useState(false);
 
const drawer = useDrawer({
    open: isOpen,
    onOpenChange: (e) => setIsOpen(e.value)
});

Animated transitions#

Combine with useMotion so the drawer and backdrop stay mounted through their exit transition before unmounting.

const drawer = useDrawer();
const motion = useMotion({ open: drawer.state.open });
 
{
    motion.present && (
        <div {...drawer.popupProps} ref={motion.ref}>
            ...
        </div>
    );
}

Non-dismissable drawer#

Force an explicit close action by disabling backdrop dismissal — useful during unsaved work.

const drawer = useDrawer({ dismissable: false });

Stacking with other overlays#

Use autoZIndex with a baseZIndex when the drawer must layer above dialogs or dropdowns.

const drawer = useDrawer({ baseZIndex: 1100, autoZIndex: true });

Styling with data attributes#

The hook exposes state through data-* attributes on each part. Use them as CSS selectors — no className juggling.

ScopePartStates
drawerrootdata-open, data-closed, data-position
drawerpopupdata-open, data-closed
[data-scope='drawer'][data-part='root'][data-open] {
    opacity: 1;
}
[data-scope='drawer'][data-part='root'][data-closed] {
    opacity: 0;
}
[data-scope='drawer'][data-part='root'][data-position='right'] {
    right: 0;
    left: auto;
}

API#

useDrawer#

NameTypeDefault
openboolean—
Specifies the visibility of the drawer.
defaultOpenboolean—
Specifies the default visibility of the drawer.
blockScrollbooleanfalse
Whether to block scrolling when the drawer is open.
dismissablebooleantrue
Whether clicking outside closes the drawer.
baseZIndexnumber0
Base zIndex value to use in layering.
autoZIndexbooleantrue
Whether to automatically manage layering.
trappedbooleantrue
When enabled, focus is trapped within the drawer (modal behavior).
position"top" | "bottom" | "left" | "right" | "full"left
Position of the drawer.
onOpenChange(event: useDrawerChangeEvent) => void—
Callback function that is called when the trigger is clicked.
onExitComplete() => void—
Callback fired after the leave (exit) transition completes.

Accessibility#

Escape closes the drawer, focus is trapped inside while open, and focus returns to the trigger on dismiss. See Primitive for full WAI-ARIA compliance details.