Beta Preview

Dialog

Dialogs are windows containing contextual information, tasks, or workflows that appear over the user interface. Depending on the kind of Dialog, further interactions may be blocked until the Dialog is acknowledged.

size 
isDismissible 
isKeyboardDismissDisabled 
import {Dialog, DialogTrigger, Button, ButtonGroup, Heading, Content, Footer, Checkbox, Image, Form, TextField} from '@react-spectrum/s2';
import heroImage from 'url:./assets/preview.png';

function Example(props) {
  return (
    <DialogTrigger>
      <Button variant="primary">Open Dialog</Button>
      <Dialog {...props}>
        {({close}) => (
          <>
            <Image slot="hero" src={heroImage} />
            <Heading slot="title">Subscribe to our newsletter</Heading>
            <Content>
              <p>Enter your information to subscribe to our newsletter and receive updates about new features and announcements.</p>
              <Form>
                <TextField label="Name" />
                <TextField label="Email" type="email" />
              </Form>
            </Content>
            <Footer>
              <Checkbox>Don't show this again</Checkbox>
            </Footer>
            <ButtonGroup>
              <Button onPress={close} variant="secondary">Cancel</Button>
              <Button onPress={close} variant="accent">Subscribe</Button>
            </ButtonGroup>
          </>
        )}
      </Dialog>
    </DialogTrigger>
  );
}

Content

Dialogs are windows containing contextual information, tasks, or workflows that appear over the user interface. Spectrum includes several pre-defined dialog components with layouts for specific use cases, or you can use CustomDialog to create a custom layout.

Standard Dialog

Use Dialog for standard dialog layouts. It supports Image, Heading, Header, Content, Footer, and ButtonGroup slots. Dismissible dialogs replace their ButtonGroup with a close button.

size 
isDismissible 
import {Dialog, DialogTrigger, Button, ButtonGroup, Heading, Header, Content, Footer, Checkbox, Image, Form, TextField} from '@react-spectrum/s2';
import heroImage from 'url:./assets/preview.png';

function Example(props) {
  return (
    <DialogTrigger>
      <Button variant="primary">Open Dialog</Button>
      <Dialog {...props}>
        {({close}) => (
          <>
            <Image slot="hero" src={heroImage} />
            <Heading slot="title">Dialog Title</Heading>
            <Header>Header</Header>
            <Content>
              <p>Standard dialog description. This should briefly communicate any additional information or context about the standard dialog title, to help users make one of the decisions offered by the buttons. Make it no more than a few short sentences.</p>
            </Content>
            <Footer>
              <Checkbox>Don't show this again</Checkbox>
            </Footer>
            <ButtonGroup>
              <Button onPress={close} variant="secondary">Cancel</Button>
              <Button onPress={close} variant="accent">Subscribe</Button>
            </ButtonGroup>
          </>
        )}
      </Dialog>
    </DialogTrigger>
  );
}

Alert Dialog

Use AlertDialog for confirmation, error messages, and other critical information that must be acknowledged.

 
 
variant 
size 
 
 
 
isPrimaryActionDisabled 
isSecondaryActionDisabled 
autoFocusButton 
import {AlertDialog, DialogTrigger, Button} from '@react-spectrum/s2';

function Example(props) {
  return (
    <DialogTrigger>
      <Button>Publish</Button>
      <AlertDialog
        {...props}
        title="Publish Document"
        primaryActionLabel="Publish"
        secondaryActionLabel="Save Draft"
        cancelLabel="Cancel"
        onPrimaryAction={() => alert('onPrimaryAction')}
        onSecondaryAction={() => alert('onSecondaryAction')}>
        {props.children}
      </AlertDialog>
    </DialogTrigger>
  );
}

Fullscreen Dialog

Use FullscreenDialog for complex workflows that require more space. It supports Heading, Header, Content, and ButtonGroup slots.

variant 
import {FullscreenDialog, DialogTrigger, Button, ButtonGroup, Heading, Header, Content} from '@react-spectrum/s2';

function Example(props) {
  return (
    <DialogTrigger>
      <Button variant="primary">Open Fullscreen</Button>
      <FullscreenDialog {...props}>
        {({close}) => (
          <>
            <Heading slot="title">Advanced Settings</Heading>
            <Header>Configure your application preferences</Header>
            <Content>
              <p>This fullscreen dialog provides ample space for complex forms, detailed content, or multi-step workflows.</p>
              <p>The content area can scroll if needed, making it perfect for lengthy forms or comprehensive settings panels.</p>
            </Content>
            <ButtonGroup>
              <Button onPress={close} variant="secondary">Cancel</Button>
              <Button onPress={close} variant="accent">Save Changes</Button>
            </ButtonGroup>
          </>
        )}
      </FullscreenDialog>
    </DialogTrigger>
  );
}

Custom Dialog

Use CustomDialog for complete control over the dialog layout.

size 
padding 
import {CustomDialog, DialogTrigger, Button, Heading, CloseButton} from '@react-spectrum/s2';
import Checkmark from '@react-spectrum/s2/illustrations/gradient/generic1/Checkmark';
import {style} from '@react-spectrum/s2/style' with {type: 'macro'};

function Example(props) {
  return (
    <DialogTrigger>
      <Button>Open dialog</Button>
      <CustomDialog {...props} size="M">
        <div className={style({display: 'flex', flexDirection: 'column', rowGap: 8, alignItems: 'center'})}>
          <Checkmark />
          <Heading slot="title" styles={style({font: 'heading-lg', textAlign: 'center', marginY: 0})}>Thank you!</Heading>
          <p className={style({font: 'body', textAlign: 'center', marginY: 0})}>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
          <CloseButton styles={style({position: 'absolute', top: 12, insetEnd: 12})} />
        </div>
      </CustomDialog>
    </DialogTrigger>
  );
}

Dialog Container

Use DialogContainer to show a dialog programmatically or mount in a different part of the JSX tree (e.g. outside a menu).

import {DialogContainer, Dialog, Button, ButtonGroup, Heading, Content, ActionMenu, MenuItem} from '@react-spectrum/s2';
import {useState} from 'react';

function DialogContainerExample() {
  let [dialogType, setDialogType] = useState(null);

  return (
    <>
      <ActionMenu onAction={setDialogType}>
        <MenuItem id="edit">Edit Item</MenuItem>
        <MenuItem id="delete">Delete Item</MenuItem>
        <MenuItem id="share">Share Item</MenuItem>
      </ActionMenu>

      <DialogContainer onDismiss={() => setDialogType(null)}>
        {dialogType === 'edit' && (
          <Dialog>
            {({close}) => (
              <>
                <Heading slot="title">Edit Item</Heading>
                <Content>Make changes to your item here.</Content>
                <ButtonGroup>
                  <Button onPress={close} variant="secondary">Cancel</Button>
                  <Button onPress={close} variant="accent">Save</Button>
                </ButtonGroup>
              </>
            )}
          </Dialog>
        )}
        {dialogType === 'delete' && (
          <Dialog>
            {({close}) => (
              <>
                <Heading slot="title">Delete Item</Heading>
                <Content>Are you sure you want to delete this item? This action cannot be undone.</Content>
                <ButtonGroup>
                  <Button onPress={close} variant="secondary">Cancel</Button>
                  <Button onPress={close} variant="negative">Delete</Button>
                </ButtonGroup>
              </>
            )}
          </Dialog>
        )}
        {dialogType === 'share' && (
          <Dialog>
            {({close}) => (
              <>
                <Heading slot="title">Share Item</Heading>
                <Content>Choose how you would like to share this item.</Content>
                <ButtonGroup>
                  <Button onPress={close} variant="secondary">Cancel</Button>
                  <Button onPress={close} variant="accent">Share</Button>
                </ButtonGroup>
              </>
            )}
          </Dialog>
        )}
      </DialogContainer>
    </>
  );
}

API

Dialog

<Dialog>
  <Image slot="hero">
  <Heading slot="title" />
  <Header />
  <Content />
  <Footer />
  <ButtonGroup />
</Dialog>
NameTypeDefault
isDismissiblebooleanDefault:
Whether the Dialog is dismissible.
size'S''M''L''XL'Default: 'M'
The size of the Dialog.
isKeyboardDismissDisabledbooleanDefault:
Whether pressing the escape key to close the dialog should be disabled.
childrenReactNode(opts: ) => ReactNodeDefault:
Children of the dialog. A function may be provided to access a function to close the dialog.
stylesDefault:
Spectrum-defined styles, returned by the style() macro.
Render Prop
close

AlertDialog

NameTypeDefault
variant'confirmation''information''destructive''error''warning'Default:
The visual style of the AlertDialog.
titlestringDefault:
The title of the AlertDialog.
childrenReactNodeDefault:
The contents of the AlertDialog.
cancelLabelstringDefault:
The label to display within the cancel button.
primaryActionLabelstringDefault:
The label to display within the confirm button.
secondaryActionLabelstringDefault:
The label to display within the secondary button.
isPrimaryActionDisabledbooleanDefault:
Whether the primary button is disabled.
isSecondaryActionDisabledbooleanDefault:
Whether the secondary button is disabled.
autoFocusButton'cancel''primary''secondary'Default:
Button to focus by default when the dialog opens.
size'S''M''L'Default: 'M'
The size of the Dialog.

FullscreenDialog

<FullscreenDialog>
  <Heading slot="title" />
  <Header />
  <Content />
  <ButtonGroup />
</FullscreenDialog>
NameTypeDefault
variant'fullscreen''fullscreenTakeover'Default: "fullscreen"
The variant of fullscreen dialog to display.
isKeyboardDismissDisabledbooleanDefault:
Whether pressing the escape key to close the dialog should be disabled.
childrenReactNode(opts: ) => ReactNodeDefault:
Children of the dialog. A function may be provided to access a function to close the dialog.
stylesDefault:
Spectrum-defined styles, returned by the style() macro.
Render Prop
close

CustomDialog

NameTypeDefault
size'S''M''L''fullscreen''fullscreenTakeover'Default:
The size of the Dialog.
isDismissiblebooleanDefault:
Whether the Dialog is dismissible.
isKeyboardDismissDisabledbooleanDefault:
Whether pressing the escape key to close the dialog should be disabled.
padding'default''none'Default: 'default'
The amount of padding around the contents of the dialog.
childrenReactNode(opts: ) => ReactNodeDefault:
Children of the dialog. A function may be provided to access a function to close the dialog.
stylesDefault:
Spectrum-defined styles, returned by the style() macro.
Render Prop
close

DialogTrigger

<DialogTrigger>
  <Button />
  <Dialog />
</DialogTrigger>
NameType
childrenReactNode

DialogContainer

<DialogContainer>
  <Dialog />
</DialogContainer>
NameType
childrenReactNode
The Dialog to display, if any.

Testing

Test utils

@react-spectrum/test-utils offers common dialog interaction utilities which you may find helpful when writing tests. To install, simply add it to your dev dependencies via your preferred package manager.

yarn add @react-spectrum/test-utils --dev

Once installed, you can access the User that @react-spectrum/test-utils provides in your test file as shown below. This user only needs to be initialized once and then can be used to generate the Dialog tester in your test cases. This gives you access to Dialog specific utilities that you can then call within your test to query for specific subcomponents or simulate common interactions. The example test case below shows how you might go about setting up the Dialog tester, use it to open and close the dialog, and verify the dialog's state after each interaction.

// Dialog.test.ts
import {render} from '@testing-library/react';
import {User} from '@react-spectrum/test-utils';

let testUtilUser = new User({interactionType: 'mouse', advanceTimer: jest.advanceTimersByTime});
// ...

it('Dialog can be opened and closed', async function () {
  // Render your test component/app and initialize the dialog tester
  let {getByTestId, getByRole} = render(
    <DialogTrigger>
      <ActionButton>Trigger</ActionButton>
      <Dialog>
        ...
      </Dialog>
    </DialogTrigger>
  );
  let button = getByRole('button');
  let dialogTester = testUtilUser.createTester('Dialog', {root: button, overlayType: 'modal'});
  await dialogTester.open();
  let dialog = dialogTester.dialog;
  expect(dialog).toBeVisible();
  await dialogTester.close();
  expect(dialog).not.toBeInTheDocument();
});

See below for the full definition of the User and the Dialog tester.

Properties

NameTypeDefault
advanceTimer['advanceTimer']Default:
A function used by the test utils to advance timers during interactions. Required for certain aria patterns (e.g. table).
interactionType['interactionType']Default: mouse
The interaction type (mouse, touch, keyboard) that the test util user will use when interacting with a component. This can be overridden at the aria pattern util level if needed.

Methods

constructor(opts: ): void
createTester<T extends >(patternName: T, opts: <T>): <T>
Creates an aria pattern tester, inheriting the options provided to the original user.

Properties

NameType
dialogHTMLElementnull
Returns the dialog if present.
triggerHTMLElement
Returns the dialog's trigger.

Methods

constructor(opts: ): void
setInteractionType(type: ['interactionType']): void
Set the interaction type used by the dialog tester.
open(opts: ): Promise<void>
Opens the dialog. Defaults to using the interaction type set on the dialog tester.
close(): Promise<void>
Closes the dialog via the Escape key.

Testing FAQ