Introducing PrimeReact v11-alpha 🎉Discover Now

useDialog

Hook that manages dialog open/close state, focus trapping, dragging, scroll lock, and z-index layering.

basic-demo

Usage#

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

useDialog manages open/close state, focus trapping, dragging, scroll locking, and z-index layering. See Primitive for a component-based API.

Features#

  • Open/close lifecycle — controlled or uncontrolled visibility with backdrop dismissal, escape handling, and return-focus on close
  • Focus management — automatic focus trap that moves focus into the dialog on open and restores it to the trigger on close
  • Positioning and layout — nine preset positions, inside/outside scroll behavior, and full-screen/maximizable rendering
  • Dragging — opt-in drag support wired through the header element for repositioning the popup
  • Scroll and layering — body scroll lock plus baseZIndex/autoZIndex coordination with other overlays
  • Imperative controls — close() and toggleMaximized() for programmatic control alongside state.open and state.maximized

Working with callbacks#

Controlled open state#

Pass open and onOpenChange to drive visibility from external state.

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

Draggable header#

Enable drag support and spread headerProps on the element that should initiate the drag.

const dialog = useDialog({ draggable: true });
 
<div {...dialog.popupProps}>
    <div {...dialog.headerProps}>Drag me</div>
</div>;

Outside vs inside scrolling#

Switch scrollBehavior to outside when the entire dialog should scroll as one unit instead of only the content region.

const dialog = useDialog({ scrollBehavior: 'outside' });

Maximizable toggle#

toggleMaximized() switches between normal and full-screen. Pair it with state.maximized to swap icons on the maximize button.

const dialog = useDialog({ fullScreen: false });
 
<button {...dialog.maximizableProps} onClick={dialog.toggleMaximized}>
    {dialog.state.maximized ? <MinimizeIcon /> : <MaximizeIcon />}
</button>;

Overlay layering#

Combine baseZIndex with autoZIndex when stacking dialogs on top of other overlays so each new dialog renders above the previous one.

const dialog = useDialog({ baseZIndex: 1000, 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
dialogrootdata-open, data-closed
dialogpopupdata-open, data-closed, data-maximized
dialogpositionerdata-position, data-scroll-behavior
dialogmaximizabledata-maximized, data-minimized
[data-scope='dialog'][data-part='positioner'] {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
 
[data-scope='dialog'][data-part='positioner'][data-position='top'] {
    align-items: flex-start;
}
 
[data-scope='dialog'][data-part='positioner'][data-scroll-behavior='outside'] {
    overflow: auto;
    align-items: flex-start;
}
 
[data-scope='dialog'][data-part='popup'][data-maximized] {
    width: 100vw;
    height: 100vh;
}
 
[data-scope='dialog'][data-part='maximizable'][data-maximized] .maximize-icon {
    display: none;
}
[data-scope='dialog'][data-part='maximizable'][data-minimized] .minimize-icon {
    display: none;
}

API#

useDialog#

NameTypeDefault
openboolean—
Specifies the visibility of the dialog.
defaultOpenboolean—
Specifies the default visibility of the dialog.
draggablebooleantrue
Enables dragging to change the position using header.
keepInViewportbooleantrue
Keeps dialog in the viewport.
minXnumber0
Minimum value for the left coordinate of dialog in dragging.
minYnumber0
Minimum value for the top coordinate of dialog in dragging.
trappedbooleantrue
When enabled, focus is trapped within the dialog (modal behavior).
modalbooleantrue
Whether the dialog is modal. When true, the positioner blocks pointer events behind it.
dismissablebooleanfalse
Specifies if clicking the modal background should hide the dialog.
closeOnEscapebooleantrue
Specifies if pressing escape key should hide the dialog.
blockScrollbooleanfalse
Whether background scroll should be blocked when dialog is visible.
baseZIndexnumber0
Base zIndex value to use in layering.
autoZIndexbooleantrue
Whether to automatically manage layering.
appendTo"body" | HTMLElement | "self"body
A valid query selector or an HTMLElement to specify where the dialog gets attached.
position"center" | "top" | "bottom" | "left" | "right" | "topleft" | "topright" | "bottomleft" | "bottomright"center
Position of the dialog.
fullScreenbooleanfalse
Whether the dialog should open in full screen (maximized) mode.
scrollBehavior"outside" | "inside"'inside'
Defines the scroll behavior of the dialog. When set to 'inside', the dialog content area scrolls. When set to 'outside', the positioner scrolls allowing the entire dialog to scroll within the viewport.
onOpenChange(event: useDialogChangeEvent) => 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 dialog when closeOnEscape is enabled, Tab cycles focus inside the dialog via focus trap, and focus returns to the trigger on close. See Primitive for full WAI-ARIA compliance details.