A ModalAlert is a simple modal dialog used to alert a user of an issue, or to request confirmation after a user-triggered action. ModalAlert overlays and blocks page content until it is dismissed by the user.

also known as AlertDialog, Prompt

Figma:

Responsive:

Adaptive:

Props

Component props
Name
Type
Default
accessibilityModalLabel
Required
string
-

String that clients such as VoiceOver will read to describe the modal. Always localize the label. See Accessibility section for more info.

children
Required
React.Node
-

Supply the element(s) that will be used as ModalAlert's main content. See the Best Practices for more info.

heading
Required
string
-

The text used for ModalAlert's heading.

onDismiss
Required
() => void
-

Callback fired when ModalAlert is dismissed by clicking on the backdrop outside of the ModalAlert or when the dismiss icon button is clicked (for default ModalAlerts).

primaryAction
Required
{
    accessibilityLabel: string,
    dataTestId?: string,
    disabled?: boolean,
    href: string,
    label: string,
    onClick?: $ElementType<React$ElementConfig<typeof ButtonLink>, "onClick">,
    rel?: "none" | "nofollow",
    role: "link",
    target?: null | "self" | "blank",
  }
| {
    accessibilityLabel: string,
    dataTestId?: string,
    disabled?: boolean,
    label: string,
    onClick?: $ElementType<React$ElementConfig<typeof Button>, "onClick">,
    role?: "button",
  }
-

Main action for users to take on ModalAlert. If href is supplied, the action will serve as a link. See GlobalEventsHandlerProvider to learn more about link navigation.
If no href is supplied, the action will be a button.
The accessibilityLabel should follow the Accessibility guidelines.

accessibilityDismissButtonLabel
string
-

Label to describe the dismiss button's purpose.

secondaryAction
{
    accessibilityLabel: string,
    dataTestId?: string,
    disabled?: boolean,
    href: string,
    label: string,
    onClick?: $ElementType<React$ElementConfig<typeof ButtonLink>, "onClick">,
    rel?: "none" | "nofollow",
    role: "link",
    target?: null | "self" | "blank",
  }
| {
    accessibilityLabel: string,
    dataTestId?: string,
    disabled?: boolean,
    label: string,
    onClick?: $ElementType<React$ElementConfig<typeof Button>, "onClick">,
    role?: "button",
  }
-

Secondary action for users to take on ModalAlert. If href is supplied, the action will serve as a link. See GlobalEventsHandlerProvider to learn more about link navigation.
If no href is supplied, the action will be a button.
The accessibilityLabel should follow the Accessibility guidelines.

type
"default" | "warning" | "error"
"default"

Determines the icon and dismiss pattern of the ModalAlert. See the warning and error variants for more info.

Usage guidelines

When to use
  • Interrupting users to get confirmation on a user-triggered action that is potentially disruptive or significantly changes the user’s content and system.
  • Interrupting users to alert them of potential issues and errors; this can be user or system-generated.
When not to use
  • Requesting large forms of information. Consider OverlayPanel or new page instead.
  • Any action that should not interrupt users from their current work stream, such as saving a Pin. Use Toast instead.
  • When alerting users of issues that can be corrected on the page or surface itself without interrupting their flow. Instead use BannerCallout or BannerSlim.

Best practices

Do

Clearly communicate what response is expected and make the action simple and straightforward, such as clicking/tapping a button to confirm.

Do

Limit the content to prevent the need to scroll at most screen sizes.

Do

Provide a way for the user to correct an error or issue via a button or a link.

Do

Explain to the user why they’ve encountered a warning or error when an action button or link is not possible.

Don't

Use language that makes it hard to understand what action is being taken, while adding additional actions that may take the user out of their existing context.

Don't

Use ModalAlert on top of another modal dialog. This can cause accessibility issues with focus states and make it hard for a user to escape and go back to the previous surface. On mobile surfaces, if a user has to confirm something triggered by a modal dialog, auto-dismiss the first dialog before presenting with the confirmation dialog.

Don't

Use ModalAlert for long and complex content or tasks, or for content that should have a dedicated surface, like login flows. If extra functionality is needed in an overlay, use Modal or OverlayPanel.

Don't

Leave it up to the user to find where to go to fix an issue.

Don't

Omit an explanation as to why a user is encountering an error or issue.

Accessibility

Labels

Make sure ModalAlerts have a clear purpose when being read by a screen reader by specifying an accessibilityModalLabel that will update the spoken text for the heading prop and give the user more context about the ModalAlert. Also ensure the accessibilityLabel is supplied when primaryAction or secondaryAction is specified. This label should provide a clear description of the action's purpose, like "Cancel board deletion".

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

Variants

Multiple actions for confirmation

This is generally triggered by user action and asks a user to confirm or cancel an action. Confirmation ModalAlerts should always have a primary and secondary button; the primary button is for confirming, and the secondary for dismissing the modal. Confirmations aren’t critical and can be dismissed by clicking outside of the modal and hitting the ESC key, in addition to using the “Cancel” buttons provided in the modal.

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  CompositeZIndex,
  FixedZIndex,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="Delete current Pin draft confirmation"
            heading="Delete this draft?"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: 'Delete draft',
              label: 'Delete',
              onClick: () => {},
              role: 'button',
            }}
            secondaryAction={{
              accessibilityLabel: 'Cancel, keep editing',
              label: 'Return to editing',
              onClick: () => {},
              role: 'button',
            }}
          >
            <Text>
              Deleting this draft cannot be undone. Are you sure you want to
              delete?
            </Text>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

Single action for acknowledgment

This is system-generated and only requires a user to dismiss the message.

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  CompositeZIndex,
  FixedZIndex,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="Unable to follow more people"
            heading="Follower limit reached"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: '',
              label: 'Got it',
              onClick: () => {},
              role: 'button',
            }}
            type="warning"
          >
            <Text>
              You&apos;ve hit a spam block and can&apos;t follow any more people
              right now. Try again later.
            </Text>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

Warning type

Warnings are used to alert a user that they need to proceed with caution. Due to their critical nature, warnings can only be dismissed by interacting with the dismiss buttons provided by the modal. If there is a way to resolve the warning, two buttons can be included. If not, only one “dismiss” button is needed.

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  CompositeZIndex,
  FixedZIndex,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="Spam link warning"
            heading="This site may lead to spam"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: 'Continue to Pin site',
              label: 'Continue to site',
              href: 'https://www.google.com',
              role: 'link',
            }}
            secondaryAction={{
              accessibilityLabel: 'Cancel navigation to site',
              label: 'Cancel',
              onClick: () => {},
              role: 'button',
            }}
            type="warning"
          >
            <Text>
              We aren&apos;t sure of the contents of this site and can&apos;t
              verify that you will find what you are looking for. Are you sure
              you want to continue?
            </Text>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  CompositeZIndex,
  FixedZIndex,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="Unable to follow more people"
            heading="Follower limit reached"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: '',
              label: 'Got it',
              onClick: () => {},
              role: 'button',
            }}
            type="warning"
          >
            <Text>
              You&apos;ve hit a spam block and can&apos;t follow any more people
              right now. Try again later.
            </Text>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

Error type

Error messages alert users of an error or a very critical issue that severely limits the user’s ability to continue. Like warnings, errors can only be dismissed by interacting with the dismiss buttons provided by the modal. If there is a way to resolve the error, two buttons can be included. If not, only one “dismiss” button is needed.

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  CompositeZIndex,
  FixedZIndex,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="API access revoked error"
            heading="Your API access has been revoked"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: 'Submit appeal to Pinterest',
              label: 'Submit an appeal',
              href: 'https://www.pinterest.com',
              role: 'link',
            }}
            secondaryAction={{
              accessibilityLabel: 'Cancel',
              label: 'Cancel',
              onClick: () => {},
              role: 'button',
            }}
            type="error"
          >
            <Text>
              You will not be able to make any API calls or create new apps.
              Pinterest Developers functionality will be limited to read-only
              data until you submit an appeal.
            </Text>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  CompositeZIndex,
  FixedZIndex,
  Layer,
  Link,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="Site blocked error"
            heading="Website blocked"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: 'Acknowledge site blocked',
              label: 'Got it',
              onClick: () => {},
              role: 'button',
            }}
            type="error"
          >
            <Text>
              We blocked the website you are trying to reach because it contains
              harmful material. Review our{' '}
              <Link
                display="inlineBlock"
                href="https://policy.pinterest.com/en/community-guidelines"
                underline="always"
              >
                content policy.
              </Link>
            </Text>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

With checkbox

Checkbox can be added to a modal that isn’t a warning or an error. Checkboxes are normally used for confirmation modals that may appear frequently in a creation or editing flow. An example is creating an Idea Pin. If the action is infrequent or highly destructive (like deleting something), do not offer an option to not show the modal again.

import { Fragment, useState } from 'react';
import {
  Box,
  Button,
  Checkbox,
  CompositeZIndex,
  FixedZIndex,
  Flex,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

const HEADER_ZINDEX = new FixedZIndex(10);
const zIndex = new CompositeZIndex([HEADER_ZINDEX]);

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);
  const [checked1, setChecked1] = useState(false);

  return (
    <Fragment>
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer zIndex={zIndex}>
          <ModalAlert
            accessibilityModalLabel="Delete current Pin draft confirmation"
            heading="Delete this page?"
            onDismiss={() => {}}
            primaryAction={{
              accessibilityLabel: 'Delete page',
              label: 'Delete page',
              onClick: () => {},
              role: 'button',
            }}
            secondaryAction={{
              accessibilityLabel: 'Cancel, keep page',
              label: 'Cancel',
              onClick: () => {},
              role: 'button',
            }}
          >
            <Flex direction="column" flex="grow" gap={4}>
              <Text>
                If you change your mind, you&apos;ll have to create this pin
                again—starting from the very beginning.
              </Text>
              <Checkbox
                checked={checked1}
                id="checkbox-show-again"
                label="Got it—don't warn me again"
                onChange={({ checked }) => setChecked1(checked)}
              />
            </Flex>
          </ModalAlert>
        </Layer>
      ) : null}
    </Fragment>
  );
}

Mobile

ModalAlert is responsive but not adaptive to mobile devices; therefore, it does not require DeviceTypeProvider.

import { useState } from 'react';
import {
  Box,
  Button,
  DeviceTypeProvider,
  Layer,
  ModalAlert,
  Text,
} from 'gestalt';

export default function Example() {
  const [showComponent, setShowComponent] = useState(true);

  return (
    <DeviceTypeProvider deviceType="mobile">
      <Box padding={2}>
        <Button
          accessibilityLabel="Show Modal"
          color="red"
          onClick={() => setShowComponent(true)}
          size="lg"
          text="Show Modal"
        />
      </Box>
      {showComponent ? (
        <Layer>
          <ModalAlert
            accessibilityModalLabel="Mobile ModalAlert example"
            heading="Heading"
            onDismiss={() => setShowComponent(false)}
            primaryAction={{
              accessibilityLabel: 'Confirm delete board',
              label: 'Yes, delete',
              onClick: () => {},
              role: 'button',
            }}
            secondaryAction={{
              accessibilityLabel: 'Cancel board deletion',
              label: 'No, keep',
              onClick: () => {},
              role: 'button',
            }}
          >
            <Box>{Array(100).fill(<Text>Content</Text>)}</Box>
          </ModalAlert>
        </Layer>
      ) : null}
    </DeviceTypeProvider>
  );
}

Writing

Do
  • Consider internationalization and how other languages may be constrained.
  • Use concise language while making it clear what is expected of the user. If the desired action can be confused with “Cancel”, add “Yes,” to the action. For example “Yes, remove”, “No, keep”
Don't
  • Pose a question in the headline that isn’t clear about the action being proposed, like “Are you sure?”
  • Use lengthy, technical jargon or local idioms that will be hard to translate to other languages.
  • Avoid exclamation marks unless the tone is celebratory; this is especially true when surfacing errors or warnings.

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

Toast
Toast provides feedback shortly after a user interaction, like a confirmation that appears when a Pin has been saved. Unlike BannerUpsells and BannerSlims, toasts overlay Page content. They also automatically disappear after a certain amount of time without being dismissed by the user.

BannerCallout
BannerCallouts are used at the top-most level of a page to communicate highest-priority information that applies to the entire page or surface. BannerCallouts can be dismissed and are also actionable.

BannerSlim
BannerSlim conveys brief information related to a specific section of a page. The message can relay success, warning, error or general information.

Modal
A generic, customizable container for modals that aren’t used as alerts and need more functionality, like form fields.