• CMSnpm version

    • Overview
    • ContentBuilder
    • InviteUserForm
    • LoginForm
    • RenewPasswordForm
  • UInpm version

    • Overview
    • Accordion
    • AlertBubble
    • AnnouncementBar
    • Avatar
    • AvatarFileInput
    • Badge
    • Button
    • ButtonGroup
    • ButtonList
    • Calendar
    • Checkbox
    • CheckboxButton
    • CheckboxInput
    • CheckboxList
    • Chip
    • ColorRadio
    • ColorRadioGroup
    • Combobox
    • DatePicker
    • DatePickerInput
    • DateRangePicker
    • DateRangePickerInput
    • DatetimePicker
    • DatetimePickerInput
    • Dialog
    • Dropdown
    • Dropzone
    • ErrorMessage
    • FileInput
    • FlashMessages
    • FormComponent
    • Icon
    • IconButton
    • ImageGallery
    • InfoBox
    • Input
    • Label
    • Layout
    • Lightbox
    • ListItem
    • Loader
    • Lozenge
    • Menu
    • Message
    • Modal
    • ✅ ModalDialog
    • ✅ ModalHeader
    • MultiCombobox
    • MultiSelect
    • Pagination
    • Paper
    • Popover
    • Radio
    • RadioGroup
    • RasterImage
    • Select
    • ✅ Tabs
    • TextInput
    • TextLink
    • Textarea
    • TimePicker
    • TimePickerInput
    • Toggle
    • Tooltip
    • Typography
  • Formnpm version

    • Overview
    • AvatarFileInput
    • CheckboxButton
    • CheckboxInput
    • CheckboxList
    • ColorRadioGroup
    • Combobox
    • DatePickerInput
    • DateRangePickerInput
    • DatetimePickerInput
    • Dropzone
    • FileInput
    • Form
    • FormRenderer
    • GpsInput
    • MoneyInput
    • MultiCombobox
    • MultiSelect
    • NumberInput
    • PasswordInput
    • RadioGroup
    • Select
    • TextInput
    • Textarea
    • TimePickerInput
    • Toggle
  • DataGridnpm version

    • Overview
    • DataGrid
    • DataGridCustomExample
    • ExportButton
    • FilterList
    • Filters
    • FiltersButton
    • FulltextInput
    • HiddenColumns
    • HiddenColumnsButton
    • Pagination
    • RowCounts
    • RowsPerPageSelect
    • SelectedRowsToolbar
    • TableV2
    • ToolbarControl
    • ToolbarCustoms
    • ToolbarTabs
  • Wysiwygnpm version

    • Overview
  • Resizernpm version

    • Overview
  • Routernpm version

    • Overview
  • Corenpm version

    • Overview
  • Core-Reactnpm version

    • Overview
  • Stylesnpm version

    • Overview
  • Localizenpm version

    • Overview
  • Analyticsnpm version

    • Overview
  • Datepickernpm version

    • Overview
  • Icons-generatornpm version

    • Overview
  • Smart-addressnpm version

    • Overview
  • E2Enpm version

    • Overview
  • E2E-Playwrightnpm version

    • Overview
View source on GitLab

Modal

Overlay / modal system for @uxf/ui: a controlled Modal component plus an imperative modal stack (ModalProvider + openModal / closeModal) with named layers.

When to use

  • Use the controlled Modal component when you own the open state locally (isOpen / onClose).
  • Use the imperative openModal API (backed by a single ModalProvider) to open modals from anywhere — event handlers, services — without threading state, and to stack modals in named layers.
  • For content, wrap children in a DialogPanel (@uxf/ui/dialog) for the standard panel, use ModalDialog for a ready-made title / body / footer layout, or ModalHeader for just the header row.

Modal wraps the lower-level Dialog and supplies its Floating UI plumbing, so you rarely need Dialog directly. There is no @uxf/form twin.

Usage

Controlled Modal

import { Button } from "@uxf/ui/button";
import { DialogPanel } from "@uxf/ui/dialog";
import { Modal } from "@uxf/ui/modal";
import { useState } from "react";

function Example() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <>
            <Button onClick={() => setIsOpen(true)}>Open</Button>
            <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
                <DialogPanel width="xs">Modal content</DialogPanel>
            </Modal>
        </>
    );
}

Imperative stack (openModal)

Mount a single ModalProvider once at the app root, wired to the shared ref:

import { getModalStackRef, ModalProvider } from "@uxf/ui/modal";

<ModalProvider ref={getModalStackRef()} />;

Then open modals from anywhere:

import { Button } from "@uxf/ui/button";
import { DialogPanel } from "@uxf/ui/dialog";
import { closeModal, openModal } from "@uxf/ui/modal";

openModal({
    children: (
        <DialogPanel width="xs">
            <Button onClick={() => closeModal()}>Close</Button>
        </DialogPanel>
    ),
    onClose: handleClose,
});

Props

Modal

Prop Type Default Description
isOpen boolean — (required) Whether the modal is rendered.
onClose () => void — (required) Called when the modal requests to close (backdrop click or ESC, unless disabled).
children ReactNode — (required) Modal content, typically a DialogPanel.
variant ModalVariant "default" Visual variant (default, drawer-right).
isBackdropCloseDisabled boolean false Disable closing on outside / backdrop press.
isEscKeyCloseDisabled boolean false Disable closing on the ESC key.
className string — Extra class on the overlay.
style CSSProperties — Inline style on the overlay.

openModal(modal, options?)

modal is a ModalProviderProps descriptor; returns the created modal instance id.

Field Type Default Description
children ReactNode — (required) Modal content.
onClose () => void — Called after the modal closes (backdrop, ESC, or programmatic).
variant ModalVariant "default" Visual variant.
isBackdropCloseDisabled boolean false Disable backdrop close.
isEscapeKeyCloseDisabled boolean false Disable ESC close.
className string — Extra class on the overlay.

Note the different spelling: the Modal component uses isEscKeyCloseDisabled, while openModal / ModalProviderProps uses isEscapeKeyCloseDisabled.

options is OpenModalOptions:

Field Type Default Description
layer ModalLayerName defaultLayer ("main") Layer to open in.
shouldReplace boolean true Replace an existing modal in the same layer instead of stacking.

Variants & states

  • Variant: default or drawer-right (right-anchored drawer on sm+). Passed through to the underlying Dialog.
  • Close triggers: by default a modal closes on backdrop press and ESC. Set isBackdropCloseDisabled / isEscKeyCloseDisabled (component) or isBackdropCloseDisabled / isEscapeKeyCloseDisabled (openModal) to disable them. In the stack, only the topmost modal responds to backdrop / ESC, so clicking inside a higher modal never closes ones underneath.

Modal stack & layers

The imperative API renders modals through the mounted ModalProvider. Service functions (from @uxf/ui/modal):

Function Description
openModal(modal, options?) Opens a modal in the given layer (or the default layer); returns its instance id.
closeModal() Closes the topmost modal (highest z-index).
closeModalLayer(layer) Closes all modals in the given layer.
closeAllModals() Closes every modal across all layers.
getModalStackRef() Returns the ref to pass to ModalProvider.
getModalRef() Deprecated alias of getModalStackRef().

By default two layers are configured (from DEFAULT_MODAL_LAYERS):

{
    layers: {
        main: { name: "main", zIndex: 100 },
        confirm: { name: "confirm", zIndex: 1000 },
    },
    defaultLayer: "main",
}
import { closeModalLayer, openModal } from "@uxf/ui/modal";
import { DialogPanel } from "@uxf/ui/dialog";

openModal({ children: <DialogPanel>Confirmation</DialogPanel> }, { layer: "confirm" });
closeModalLayer("confirm");

Known limitation: the shipped ModalProvider assigns every opened modal the default layer's z-index regardless of the layer option, so custom per-layer z-index values do not currently affect stacking (modals paint in the order they were opened). Layer names still work for targeted closing via closeModalLayer.

Custom layers

Provide a custom configuration through ModalLayerConfigProvider (optional — the default config is used when absent):

import { ModalLayerConfigProvider, ModalLayersConfiguration, ModalProvider, getModalStackRef } from "@uxf/ui/modal";

const customModalLayers: ModalLayersConfiguration = {
    layers: {
        main: { name: "main", zIndex: 100 },
        confirm: { name: "confirm", zIndex: 1000 },
        notification: { name: "notification", zIndex: 9000 },
    },
    defaultLayer: "main",
};

<ModalLayerConfigProvider config={customModalLayers}>
    <ModalProvider ref={getModalStackRef()} />
</ModalLayerConfigProvider>;

Add custom layer names to the type system with declaration merging on ModalLayers (@uxf/ui/modal/theme):

// modal.d.ts (in your project)
declare module "@uxf/ui/modal/theme" {
    interface ModalLayers {
        notification: true;
    }
}

ModalLayerName (keyof ModalLayers) then autocompletes the new name in openModal(..., { layer }) and closeModalLayer.

Requirements

Modal renders through Dialog, so import the dialog stylesheet once in your global CSS:

@import url("@uxf/ui/css/dialog.css");

Also requires the global @uxf/ui token layer, set up once per app — see @uxf/ui setup.

For the imperative API, mount exactly one <ModalProvider ref={getModalStackRef()} /> at the app root.

Default
Open in new tab
ModalProvider
Open in new tab
ModalProviderWithLayers
Open in new tab