useDialog
Hook that manages dialog open/close state, focus trapping, dragging, scroll lock, and z-index layering.
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.bodyuseDialog 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/outsidescroll 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/autoZIndexcoordination with other overlays - Imperative controls —
close()andtoggleMaximized()for programmatic control alongsidestate.openandstate.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.
| Scope | Part | States |
|---|---|---|
dialog | root | data-open, data-closed |
dialog | popup | data-open, data-closed, data-maximized |
dialog | positioner | data-position, data-scroll-behavior |
dialog | maximizable | data-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#
| Name | Type | Default |
|---|---|---|
open | boolean | — |
| Specifies the visibility of the dialog. | ||
defaultOpen | boolean | — |
| Specifies the default visibility of the dialog. | ||
draggable | boolean | true |
| Enables dragging to change the position using header. | ||
keepInViewport | boolean | true |
| Keeps dialog in the viewport. | ||
minX | number | 0 |
| Minimum value for the left coordinate of dialog in dragging. | ||
minY | number | 0 |
| Minimum value for the top coordinate of dialog in dragging. | ||
trapped | boolean | true |
| When enabled, focus is trapped within the dialog (modal behavior). | ||
modal | boolean | true |
| Whether the dialog is modal. When true, the positioner blocks pointer events behind it. | ||
dismissable | boolean | false |
| Specifies if clicking the modal background should hide the dialog. | ||
closeOnEscape | boolean | true |
| Specifies if pressing escape key should hide the dialog. | ||
blockScroll | boolean | false |
| Whether background scroll should be blocked when dialog is visible. | ||
baseZIndex | number | 0 |
| Base zIndex value to use in layering. | ||
autoZIndex | boolean | true |
| 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. | ||
fullScreen | boolean | false |
| 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.