packages/react-aria-components/docs/Dialog.mdx
{/* Copyright 2020 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */}
import {Layout} from '@react-spectrum/docs'; export default Layout;
import docs from 'docs:react-aria-components'; import overlayStatelyDocs from 'docs:@react-stately/overlays'; import {PropTable, HeaderInfo, TypeLink, PageDescription, StateTable, ContextTable, VersionBadge, ClassAPI} from '@react-spectrum/docs'; import styles from '@react-spectrum/docs/src/docs.css'; import packageData from 'react-aria-components/package.json'; import Anatomy from '/packages/react-aria/docs/dialog/anatomy.svg'; import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; import {Divider} from '@react-spectrum/divider'; import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; import {ExampleList} from '@react-spectrum/docs/src/ExampleList'; import {Keyboard} from '@react-spectrum/text'; import {StarterKits} from '@react-spectrum/docs/src/StarterKits';
<PageDescription>{docs.exports.Dialog.description}</PageDescription>
<HeaderInfo packageData={packageData} componentNames={['Dialog']} sourceData={[ {type: 'W3C', url: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/'} ]} />
import {DialogTrigger, Modal, Dialog, Button, Heading, TextField, Label, Input} from 'react-aria-components';
<DialogTrigger>
<Button>Sign up…</Button>
<Modal>
<Dialog>
<form>
<Heading slot="title">Sign up</Heading>
<TextField autoFocus>
<Label>First Name</Label>
<Input />
</TextField>
<TextField>
<Label>Last Name</Label>
<Input />
</TextField>
<Button slot="close" style={{marginTop: 8}}>
Submit
</Button>
</form>
</Dialog>
</Modal>
</DialogTrigger>
.react-aria-Dialog {
outline: none;
padding: 30px;
max-height: inherit;
box-sizing: border-box;
overflow: auto;
.react-aria-Heading[slot=title] {
line-height: 1em;
margin-top: 0;
}
}
The HTML <dialog> element
can be used to build dialogs. However, it is not yet widely supported across browsers, and
building fully accessible custom dialogs from scratch is very difficult and error prone.
Dialog helps achieve accessible dialogs that can be styled as needed.
dialog or alertdialog with ARIA. The dialog is automatically labeled by a nested <Heading> element. Content outside the dialog is hidden from assistive technologies while it is open.A dialog consists of a container element and an optional title and close button. It can be placed within a Modal or Popover, to create modal dialogs, popovers, and other types of overlays. A DialogTrigger can be used to open a dialog overlay in response to a user action, e.g. clicking a button.
import {DialogTrigger, Modal, Dialog, Button, Heading} from 'react-aria-components';
<DialogTrigger>
<Button />
<Modal>
<Dialog>
<Heading slot="title" />
<Button slot="close" />
</Dialog>
</Modal>
</DialogTrigger>
If a dialog does not have a visible heading element, an aria-label or aria-labelledby prop must be passed instead to identify the element to assistive technology.
To help kick-start your project, we offer starter kits that include example implementations of all React Aria components with various styling solutions. All components are fully styled, including support for dark mode, high contrast mode, and all UI states. Each starter comes with a pre-configured Storybook that you can experiment with, or use as a starting point for your own component library.
<StarterKits component="dialog" tailwindComponent="alertdialog" />A Dialog may be placed within a Popover to display it in context with a trigger element.
import {Popover, OverlayArrow} from 'react-aria-components';
<DialogTrigger>
<Button aria-label="Help">ⓘ</Button>
<Popover>
<OverlayArrow>
<svg width={12} height={12} viewBox="0 0 12 12"><path d="M0 0 L6 6 L12 0" /></svg>
</OverlayArrow>
<Dialog>
<Heading slot="title">Help</Heading>
<p>For help accessing your account, please contact support.</p>
</Dialog>
</Popover>
</DialogTrigger>
Alert dialogs are a special type of dialog meant to present a prompt that the user must confirm before an action proceeds. An alert dialog may also behave differently with assistive technologies, such as playing a system alert sound when opening. Use the role="alertdialog" prop on the <Dialog> element to make an alert dialog.
<DialogTrigger>
<Button>Delete…</Button>
<Modal>
<Dialog role="alertdialog">
{({close}) => (
<>
<Heading slot="title">Delete file</Heading>
<p>This will permanently delete the selected file. Continue?</p>
<div style={{display: 'flex', gap: 8}}>
<Button onPress={close}>Cancel</Button>
<Button onPress={close}>Delete</Button>
</div>
</>
)}
</Dialog>
</Modal>
</DialogTrigger>
DialogTrigger works out of the box with any pressable React Aria component (e.g. Button, Link, etc.). Custom trigger elements such as third party components and other DOM elements are also supported by wrapping them with the <Pressable> component, or using the usePress hook.
import {Pressable} from 'react-aria-components';
<DialogTrigger>
<Pressable>
<span role="button">Custom trigger</span>
</Pressable>
<Modal>
<Dialog>
<Heading slot="title">Dialog</Heading>
<p>This dialog was triggered by a custom button.</p>
<Button slot="close">Close</Button>
</Dialog>
</Modal>
</DialogTrigger>
Note that any <Pressable> child must have an interactive ARIA role or use an appropriate semantic HTML element so that screen readers can announce the trigger. Trigger components must forward their ref and spread all props to a DOM element.
const CustomTrigger = React.forwardRef((props, ref) => (
<button {...props} ref={ref} />
));
A <Heading> accepts all HTML attributes.
A <Button slot="close"> element can be placed inside a <Dialog> to close it when pressed.
React Aria components can be styled in many ways, including using CSS classes, inline styles, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc. By default, all components include a builtin className attribute which can be targeted using CSS selectors. These follow the react-aria-ComponentName naming convention.
.react-aria-Dialog {
/* ... */
}
A custom className can also be specified on any component. This overrides the default className provided by React Aria with your own.
<Dialog className="my-dialog">
</Dialog>
The selectors for each component used in a Dialog are documented below.
The DialogTrigger component does not render any DOM elements (it only passes through its children) so it does not support styling. If you need a wrapper element, add one yourself inside the <DialogTrigger>.
<DialogTrigger>
<div className="my-dialog-trigger">
</div>
</DialogTrigger>
A Dialog can be targeted with the .react-aria-Dialog CSS selector, or by overriding with a custom className.
A Heading can be targeted with the .react-aria-Heading CSS selector, or by overriding with a custom className.
DialogTrigger and Dialog pass props to their child components, such as the trigger button and modal overlay, via their associated contexts. These contexts are exported so you can also consume them in your own custom components. This enables you to reuse existing components from your app or component library together with React Aria Components.
<ContextTable components={['Button', 'Dialog', 'Modal', 'Popover', 'Heading']} docs={docs} />
This example consumes from HeadingContext in an existing styled heading component to make it compatible with React Aria Components. The <TypeLink links={docs.links} type={docs.exports.useContextProps} /> hook merges the local props and ref with the ones provided via context by Dialog.
import type {HeadingProps} from 'react-aria-components';
import {HeadingContext, useContextProps} from 'react-aria-components';
const MyCustomHeading = React.forwardRef((props: HeadingProps, ref: React.ForwardedRef<HTMLHeadingElement>) => {
// Merge the local props and ref with the ones provided via context.
///- begin highlight -///
[props, ref] = useContextProps(props, ref, HeadingContext);
///- end highlight -///
// ... your existing Heading component
return <h2 {...props} ref={ref} />;
});
Now you can use MyCustomHeading within a Dialog, in place of the builtin React Aria Components Heading.
<Dialog>
<MyCustomHeading>Dialog title</MyCustomHeading>
</Dialog>
DialogTrigger provides an <TypeLink links={overlayStatelyDocs.links} type={overlayStatelyDocs.exports.OverlayTriggerState} /> object to its children via OverlayTriggerStateContext. This can be used to access and manipulate the dialog trigger's state.
This example shows a CloseButton component that can be placed within a DialogTrigger to close the overlay.
import {OverlayTriggerStateContext} from 'react-aria-components';
function CloseButton() {
/*- begin highlight -*/
let state = React.useContext(OverlayTriggerStateContext)!;
/*- end highlight -*/
return <Button onPress={() => state.close()}>Close</Button>;
}
<DialogTrigger>
<Button>About</Button>
<Modal isDismissable>
<Dialog>
<Heading slot="title">About</Heading>
<p>Copyright © 2023 Adobe. All rights reserved.</p>
<CloseButton />
</Dialog>
</Modal>
</DialogTrigger>
If you need to customize things further, such as accessing internal state or customizing DOM structure, you can drop down to the lower level Hook-based API. See useDialog for more details.