Skip to Content

Dialog

Dialogs inform users about a task and can contain critical information, require decisions, or involve multiple tasks.

import * as React from 'react';
// import * as ReactDOM from 'react-dom';
import { Dialog, Button } from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
        Open dialog
      </Button>
      <Dialog
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        closeOnEsc
        closeOnExternalClick
        preventDocumentScroll
        trapFocus
        setFocus
        isDismissible
        portal
      >
        <Dialog.Backdrop />
        <Dialog.Main>
          <Dialog.TitleBar titleText='Dialog' />
          <Dialog.Content>
            A dialog informs users about a task and can contain critical
            information, require decisions, or involve multiple tasks. Dialogs
            appear in front of app content to provide critical information or
            ask for a decision.
          </Dialog.Content>
          <Dialog.ButtonBar>
            <Button
              styleType='high-visibility'
              onClick={() => setIsOpen(false)}
            >
              Primary
            </Button>
            <Button onClick={() => setIsOpen(false)}>Secondary</Button>
          </Dialog.ButtonBar>
        </Dialog.Main>
      </Dialog>
    </>
  );
};

A dialog informs users about a task and can contain critical information, require decisions, or involve multiple tasks. Dialogs appear in front of app content to provide critical information or ask for a decision. Dialogs disable all app functionality when they appear, and remain on screen until confirmed, dismissed, or a required action has been taken.

Dialogs are purposefully interruptive, so they should be used sparingly.

Dialog VS Modal

The terms dialog and modal are often used interchangeably. Within iTwinUI we differentiate the two terms like so:

  • Non-modal dialog: a non-modal dialog is the box of content that appears above the rest of the page content. The user can still interact with the page content.
  • Modal dialog: a modal dialog is the combination of a dialog with a backdrop that appears above the rest of the page content. This backdrop disables interaction with the page content until the dialog is dismissed.
import * as React from 'react';
import {
  Modal,
  Button,
  ModalContent,
  ModalButtonBar,
} from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <div className='demo-container'>
        <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
          Open modal dialog
        </Button>
      </div>

      <Modal isOpen={isOpen} title={'Modal'} onClose={() => setIsOpen(false)}>
        <ModalContent>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat.
        </ModalContent>
        <ModalButtonBar>
          <Button styleType='high-visibility' onClick={() => setIsOpen(false)}>
            Primary
          </Button>
          <Button onClick={() => setIsOpen(false)}>Secondary</Button>
        </ModalButtonBar>
      </Modal>
    </>
  );
};

Variants

Dismissible

A dismissible dialog contains information that is not required to complete before being able to proceed.

import * as React from 'react';
// import * as ReactDOM from 'react-dom';
import {
  Dialog,
  Button,
  LabeledInput,
  LabeledTextarea,
} from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
        Open dismissible dialog
      </Button>
      <Dialog
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        setFocus={false}
        closeOnEsc
        closeOnExternalClick
        isDismissible
        portal
      >
        <Dialog.Backdrop />
        <Dialog.Main>
          <Dialog.TitleBar titleText='New message' />
          <Dialog.Content>
            <LabeledInput label='Subject' />
            <LabeledTextarea label='Message' />
          </Dialog.Content>
          <Dialog.ButtonBar>
            <Button
              styleType='high-visibility'
              onClick={() => setIsOpen(false)}
            >
              Submit
            </Button>
            <Button onClick={() => setIsOpen(false)}>Save draft</Button>
          </Dialog.ButtonBar>
        </Dialog.Main>
      </Dialog>
    </>
  );
};

The dialog can be dismissed multiple different ways:

  • Clicking one of the action buttons.
  • Clicking on “X” button.
  • Pressing the ESC key on your keyboard.
  • Clicking outside the dialog window.

The “X” icon bears the borderless button style which appears on cursor hover.

Non-dismissible

A non dismissible dialog contains important information that must be completed. The only way to dismiss a non dismissible dialog is by completing the action.

import * as React from 'react';
// import * as ReactDOM from 'react-dom';
import { Dialog, Button } from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
        Open non-dismissible dialog
      </Button>
      <Dialog
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        setFocus={false}
        isDismissible={false}
        portal
      >
        <Dialog.Backdrop />
        <Dialog.Main>
          <Dialog.TitleBar titleText='Empty trash' />
          <Dialog.Content>
            Are you sure you want to permanently erase the items in the trash?
            You can't undo this action.
          </Dialog.Content>
          <Dialog.ButtonBar>
            <Button
              styleType='high-visibility'
              onClick={() => setIsOpen(false)}
            >
              Empty trash
            </Button>
            <Button onClick={() => setIsOpen(false)}>Cancel</Button>
          </Dialog.ButtonBar>
        </Dialog.Main>
      </Dialog>
    </>
  );
};

Draggable

These dialogs have a defined header that can be clicked and moved around the screen at the user’s leisure, just like a browser window. This dialog can also be resized.

import * as React from 'react';
// import * as ReactDOM from 'react-dom';
import {
  Dialog,
  Button,
  LabeledInput,
  LabeledTextarea,
} from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
        Open draggable dialog
      </Button>

      <Dialog
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        setFocus={false}
        closeOnEsc
        isDismissible
        isDraggable
        isResizable
        portal
      >
        <Dialog.Main>
          <Dialog.TitleBar titleText='New message' />
          <Dialog.Content>
            <LabeledInput label='Subject' />
            <LabeledTextarea label='Message' />
          </Dialog.Content>
          <Dialog.ButtonBar>
            <Button
              styleType='high-visibility'
              onClick={() => setIsOpen(false)}
            >
              Submit
            </Button>
            <Button onClick={() => setIsOpen(false)}>Save draft</Button>
          </Dialog.ButtonBar>
        </Dialog.Main>
      </Dialog>
    </>
  );
};

Full page

A full page dialog appears on top of the content and occupy the entire page’s space. It is used primarily for pages where users will customize settings.

import * as React from 'react';
import {
  Modal,
  Button,
  ModalContent,
  ModalButtonBar,
  LabeledInput,
  LabeledTextarea,
} from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
        Open full page dialog
      </Button>
      <Modal
        isOpen={isOpen}
        title={'New message'}
        styleType='fullPage'
        onClose={() => setIsOpen(false)}
      >
        <ModalContent>
          <LabeledInput label='Subject' />
          <LabeledTextarea label='Message' />
        </ModalContent>
        <ModalButtonBar>
          <Button styleType='high-visibility' onClick={() => setIsOpen(false)}>
            Submit
          </Button>
          <Button onClick={() => setIsOpen(false)}>Save draft</Button>
        </ModalButtonBar>
      </Modal>
    </>
  );
};

The two buttons at the bottom right are always pinned in that same area, regardless of content. If there is more content than there is available space in the content area, a scrollbar can be included to make the content area scrollable.

As specified earlier, full-page dialogs will house various settings options as shown in the example below.

Placement

Using the placement prop, a dialog can be adjusted to sit in one of the four corners of the page. Accepted values:

  • top-left
  • top-right
  • bottom-left
  • bottom-right
import * as React from 'react';
// import * as ReactDOM from 'react-dom';
import { Dialog, Button } from '@itwin/itwinui-react';

export default () => {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <>
      <Button styleType='high-visibility' onClick={() => setIsOpen(true)}>
        Open dialog
      </Button>
      <Dialog
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        closeOnEsc
        closeOnExternalClick
        preventDocumentScroll
        trapFocus
        setFocus
        isDismissible
        portal
        placement='top-left'
      >
        <Dialog.Backdrop />
        <Dialog.Main>
          <Dialog.TitleBar titleText='Dialog' />
          <Dialog.Content></Dialog.Content>
          <Dialog.ButtonBar>
            <Button
              styleType='high-visibility'
              onClick={() => setIsOpen(false)}
            >
              Primary
            </Button>
            <Button onClick={() => setIsOpen(false)}>Secondary</Button>
          </Dialog.ButtonBar>
        </Dialog.Main>
      </Dialog>
    </>
  );
};

Usage guidelines

Do:

  • Use dialogs for important warnings, as a way to prevent or correct critical errors.
  • Simplify the workflow. Use dialogs to ask for information that, when provided, could significantly lessen users’ work or effort.
  • Limit the dialog to a single purpose. Limit the interaction to one, straightforward task.
  • Keep it short. Be brief and concise in your content.
  • Try displaying your information in a different way. See alternatives for more information.

Don’t:

  • Open a dialog from a dialog.
  • Have multiple steps within a dialog.
  • Present dialogs unless prompted by the user.
  • Use a login within a dialog windows. You can’t link to them and some password managers can’t pre-fill them because the dialogs are hidden.

Alternative options

Dialogs can easily be misused or overused. Here are some alternative options to consider:

  • Present your content inline to be less disruptive, such as the message underneath an input.
  • Use expanding elements such as the expandable block, tooltips, or the information panel.
  • Lead the user to a different page to isolate the interaction without losing access to functionality such as navigation.
  • Inform the user with a less intrusive notification such as a toast. Toasts are less intrusive and are preferred to a dialog for interactions such as undoing an action.

Props

Prop Description Default
title
Modal title.
ReactNode
onClose
Handler that is called when Modal is closed.
(event?: SyntheticEvent<Element, Event>) => void
isDismissible
Flag whether modal is dismissible. If false, you can't close it.
boolean
true
closeOnExternalClick
Flag whether modal should be closed on background overlay press.
boolean
true
closeOnEsc
Flag whether modal should be closed on Escape key press.
boolean
true
onKeyDown
Handle key press. Returns the keyboard event.
KeyboardEventHandler<Element>
portal
If true, the dialog will be portaled into a
inside the nearest ThemeProvider.
Can be set to an object with a to property to portal into a specific element. If to/to() === null/undefined, the default behavior will be used (i.e. as if portal is not passed).
boolean | { to: HTMLElement | (() => HTMLElement); }
true
children
Content of the modal.
ReactNode
titleBarProps
Props for customizing the title bar element.
Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ref?: Ref<HTMLDivElement>; }
wrapperProps
Props for customizing the dialog-wrapper element.
Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">
backdropProps
Props for customizing the backdrop element.
Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ref?: Ref<HTMLDivElement>; }
isOpen
Flag whether dialog should be shown.
It is recommended to directly pass the boolean condition to this prop instead of rendering the Dialog conditionally with isOpen hard-coded to true. One benefit of this is getting an exit animation.
boolean
false
styleType
Type of the dialog.
"default" | "fullPage"
'default'
as
"symbol" | "object" | "div" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | ... 158 more ... | FunctionComponent<...>