Next Chapter Begins 🚀Read the full story

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

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.