Dialog
Dialogs inform users about a task and can contain critical information, require decisions, or involve multiple tasks.
import * as React from 'react';
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 {
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 {
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<...> |