Acronis UIKit
Components

Dialog

A modal overlay that interrupts the user to show focused content or request a decision.

Usage

import { Dialog } from '@acronis-platform/ui-react';

Dialog is the sanctioned way to build a dialog. A single variant prop selects one of seven canned use-cases — default, rename, save changes, reset password, discard changes, accept, read-only — each with its own title, body copy, and footer buttons, so product code doesn't hand-assemble the same header/body/footer boilerplate every time. children overrides the body slot; hasLoading shows a spinner overlay across the body + footer. It's built internally on Base UI's Dialog primitive (focus trap, scroll lock, Esc/outside-press dismissal, and ARIA come from Base UI), but that primitive is not itself exported — if none of the seven variants fit your use case, raise it with the design system team rather than reaching for a lower-level API.

An eighth wide variant is a legacy escape hatch — no Figma preset, meant to be paired with a fully custom title/children/footer — kept only for backward compatibility with pre-existing wider dialogs; prefer one of the seven canned variants for new work.

Examples

A canned use-case, controlled open state. onPrimaryAction fires when the primary footer button is clicked — the dialog doesn't close on its own, so close it yourself once the action completes. (read-only is the one exception: its single "Done" button both fires onPrimaryAction and dismisses the dialog.)

const [open, setOpen] = useState(false);

<Dialog
  variant="discard changes"
  open={open}
  onOpenChange={setOpen}
  onPrimaryAction={() => {
    discardChanges();
    setOpen(false);
  }}
/>;

Override the body slot while keeping the header/footer chrome:

<Dialog variant="default" defaultOpen>
  <p>Any custom body content.</p>
</Dialog>

Busy state — the body and footer become inert (unfocusable, unclickable) and the popup gets aria-busy="true" while hasLoading is set; loadingLabel overrides the overlay's accessible name:

<Dialog variant="save changes" hasLoading loadingLabel="Enregistrement…" open />

Localize the canned copy — title, secondaryLabel, primaryLabel, closeLabel, and loadingLabel all override the variant's default English string:

<Dialog
  variant="discard changes"
  title="Modifications non enregistrées"
  secondaryLabel="Retour"
  primaryLabel="Confirmer"
  closeLabel="Fermer"
  open
/>

rename, discard changes, and accept embed a generic "object name" placeholder in their canned title/body. Pass objectName to interpolate the real name (e.g. a file being renamed) instead of overriding title/children by hand:

<Dialog variant="rename" objectName="Q3 Report.xlsx" open onOpenChange={setOpen} />

The rename variant's text field accessible name is also overridable via objectNameLabel (default 'Object name'):

<Dialog variant="rename" objectNameLabel="Nom de l'objet" open onOpenChange={setOpen} />

The popup always keeps a 48px minimum margin from every viewport edge — each size is capped at calc(100dvw - 96px) wide and the popup at calc(100dvh - 96px) tall — so a narrow or short viewport shrinks the dialog rather than letting it run off-screen. Within that clamp the header and footer hold their token heights and never compress; the body absorbs the remaining space and scrolls on its own, so don't add another vertical scroll container around children — that would create two competing vertical scrollbars. Short body content stays vertically centered, while content taller than the available space scrolls with the top edge always reachable. The popup's width is likewise fixed to whichever size is active, never content-driven — wide, non-wrapping content (e.g. a table that doesn't wrap) scrolls horizontally within the body instead of widening the popup. A component with its own horizontal-only scroll region (e.g. DataTable) composes fine here since it doesn't compete with the body's vertical scroll:

<Dialog variant="read-only" open>
  {longLegalText.map((paragraph) => (
    <p key={paragraph}>{paragraph}</p>
  ))}
</Dialog>

When the dialog renders inside an isolated container (e.g. a shadow root), pass a portalContainer so the popup inherits that scope's styles:

<Dialog portalContainer={mountNode}>{/* … */}</Dialog>

keepMounted leaves the popup markup in the DOM while the dialog is closed (hidden, and not exposed as a dialog to assistive tech) instead of unmounting it — reach for it when the body holds state or an expensive child you don't want torn down and rebuilt on every open:

<Dialog keepMounted open={open} onOpenChange={setOpen}>
  <ExpensiveForm />
</Dialog>

Hide the header and/or footer chrome for a body-only dialog. Both default to true; this is beyond the strict Figma contract (all seven canned variants always show both). When the header is hidden, the title still renders off-screen so the dialog keeps an accessible name:

<Dialog hasHeader={false} open>
  <p>Body-only content — no title bar, no close button.</p>
</Dialog>

Legacy wide variant — a free-form footer for pre-existing wider dialogs that don't fit one of the seven canned use-cases:

import { Button, DialogClose } from '@acronis-platform/ui-react';

<Dialog
  variant="wide"
  size="large"
  title="Configure discovery agent"
  open
  footer={
    <>
      <DialogClose render={<Button variant="ghost">Cancel</Button>} />
      <Button>Configure</Button>
    </>
  }
>
  <p>Any custom body content.</p>
</Dialog>

API Reference

Prop

Type

Edit on GitHub

On this page