Antares
Components

Modal

The Modal component presents a dialog window over the page that focuses the user's attention on a single task or piece of information.

Use ModalTrigger to wire a trigger element (typically a Button) to a Modal. The Modal exposes high-level slots for title, description, actions, and media, while still accepting arbitrary children for custom content.

Installation

npm install --save @godaddy/antares

Props

The Modal component accepts the following props:

NameTypeDefault
title?ReactNode

Title of the modal.

description?ReactNode

Description of the modal.

overlayProps?RACModalOverlayProps | undefined

Additional props to pass to the overlay container.

containerProps?Omit<FlexProps, "as"> | undefined

Additional props to pass to the modal container.

className?string | undefined'react-aria-Dialog'

Additional class name for the modal.

children?ReactNode

Content of the modal.

actions?ReactNode

Actions to display in the modal.

actionProps?FlexProps | undefined

Additional props to pass to the actions container.

media?ReactNode

Media element (e.g. `<img />`) to display in the modal.

mediaProps?FlexProps | undefined

Additional props to pass to the media container.

mediaVariant?"inset" | "full" | undefined

Variant of the media container.

mediaPosition?"start" | "end" | undefined

Position of the media container.

mediaDirection?"column" | "row" | undefined

Direction of the media container.

titleProps?TextProps | undefined

Additional props to pass to the title.

descriptionProps?TextProps | undefined

Additional props to pass to the description.

closeProps?ButtonProps | undefined

Additional props to pass to the close button.

isDismissable?boolean | undefinedtrue

Whether the modal can be dismissed via overlay click or Escape key.

centered?boolean | undefined

Whether the modal content is centered.

role?"dialog" | "alertdialog" | undefined'dialog'

The accessibility role for the dialog.

id?string | undefined

The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).

style?React.CSSProperties | undefined

The inline [style](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element.

slot?string | null | undefined

A slot name for the component. Slots allow the component to receive props from a parent component. An explicit `null` value indicates that the local props completely override all props received from a parent.

render?DOMRenderFunction<"section", undefined> | undefined

Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.

Examples

Basic usage

The default modal exposes a title, description, and children content area. Open and close state is managed automatically by ModalTrigger.

import { ModalTrigger, Modal, Button, Flex } from '@godaddy/antares';export function DefaultExample() {  return (    <ModalTrigger>      <Button variant="primary">Open modal</Button>      <Modal        title="Modal title"        description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther."      >        <Flex padding="md" elevation="card" justifyContent="center">          This is the children content        </Flex>      </Modal>    </ModalTrigger>  );}

Controlled

Pass isOpen and onOpenChange to ModalTrigger to control the open state externally.

The modal is currently closed.
import { ModalTrigger, Modal, Button, Flex, Text } from '@godaddy/antares';import { useState } from 'react';export function ControlledExample() {  const [isOpen, setIsOpen] = useState(false);  return (    <Flex direction="column" gap="sm" alignItems="start">      <ModalTrigger isOpen={isOpen} onOpenChange={setIsOpen}>        <Button variant="primary">Open modal</Button>        <Modal          title="Modal title"          description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther."          actions={[            <Button key="close" slot="close" variant="primary">              Close            </Button>          ]}        />      </ModalTrigger>      <Text>The modal is currently {isOpen ? 'open' : 'closed'}.</Text>      <Button variant="secondary" onPress={() => setIsOpen(true)}>        Open from outside      </Button>    </Flex>  );}

With actions

Render footer buttons via the actions prop. Use slot="close" on a button to close the modal automatically when pressed.

import { ModalTrigger, Modal, Button } from '@godaddy/antares';export function WithActionsExample() {  return (    <ModalTrigger>      <Button variant="primary">Open modal</Button>      <Modal        title="Modal title"        description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther."        actions={[          <Button key="cancel" slot="close" variant="secondary">            Cancel          </Button>,          <Button key="confirm" slot="close" variant="primary">            Confirm          </Button>        ]}      />    </ModalTrigger>  );}

Alignment

Use centered to center the title and description, and actionProps to control the alignment and direction of the action buttons.

Modal alignment
Actions alignment
Actions direction
import { ModalTrigger, Modal, Button, RadioGroup, Radio, Flex } from '@godaddy/antares';import { useState } from 'react';export function AlignmentExample() {  const [alignment, setAlignment] = useState<string>();  const [actionsAlignment, setActionsAlignment] = useState<string>();  const [actionsDirection, setActionsDirection] = useState<string>();  return (    <Flex direction="column" gap="sm" alignItems="start">      <ModalTrigger>        <Button variant="primary">Open modal</Button>        <Modal          title="Modal title"          description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther."          centered={alignment === 'center'}          actionProps={{            ...(actionsAlignment ? { justifyContent: actionsAlignment } : {}),            ...(actionsDirection ? { direction: actionsDirection as 'row' | 'column' } : {})          }}          actions={[            <Button key="cancel" slot="close" variant="secondary">              Cancel            </Button>,            <Button key="confirm" slot="close" variant="primary">              Confirm            </Button>          ]}        />      </ModalTrigger>      <RadioGroup orientation="horizontal" label="Modal alignment" value={alignment} onChange={setAlignment}>        <Radio value="">Start (Default)</Radio>        <Radio value="center">Centered</Radio>      </RadioGroup>      <RadioGroup        orientation="horizontal"        label="Actions alignment"        value={actionsAlignment}        onChange={setActionsAlignment}      >        <Radio value="start">Start</Radio>        <Radio value="center">Centered</Radio>        <Radio value="">End (Default)</Radio>      </RadioGroup>      <RadioGroup        orientation="horizontal"        label="Actions direction"        value={actionsDirection}        onChange={setActionsDirection}      >        <Radio value="row">Row (Default)</Radio>        <Radio value="column">Column</Radio>      </RadioGroup>    </Flex>  );}

With media

Pass a media element to display imagery alongside the content. Configure its layout with mediaVariant (full or inset), mediaDirection (column or row), and mediaPosition (start or end).

Variant
Direction
Position
import { ModalTrigger, Modal, Button, RadioGroup, Radio, Flex } from '@godaddy/antares';import { useState } from 'react';export function WithMediaExample() {  const [mediaVariant, setMediaVariant] = useState<string>();  const [mediaDirection, setMediaDirection] = useState<string>();  const [mediaPosition, setMediaPosition] = useState<string>();  return (    <Flex direction="column" gap="sm" alignItems="start">      <ModalTrigger>        <Button variant="primary">Open modal</Button>        <Modal          title="Modal title"          description="She expressed her gratitude again, but as it was too painful a subject to each, to be dwelt on farther."          media={            <Flex style={{ width: '100%', height: '100%', minWidth: 350, minHeight: 200, backgroundColor: 'teal' }} />          }          mediaVariant={mediaVariant as 'inset' | 'full' | undefined}          mediaDirection={mediaDirection as 'column' | 'row' | undefined}          mediaPosition={mediaPosition as 'start' | 'end' | undefined}          actions={[            <Button key="cancel" slot="close" variant="secondary">              Cancel            </Button>,            <Button key="confirm" slot="close" variant="primary">              Confirm            </Button>          ]}        >          <Flex padding="md" elevation="card" justifyContent="center">            This is the children content          </Flex>        </Modal>      </ModalTrigger>      <RadioGroup orientation="horizontal" label="Variant" value={mediaVariant} onChange={setMediaVariant}>        <Radio value="inset">Inset</Radio>        <Radio value="full">Full (Default)</Radio>      </RadioGroup>      <RadioGroup orientation="horizontal" label="Direction" value={mediaDirection} onChange={setMediaDirection}>        <Radio value="row">Row</Radio>        <Radio value="column">Column (Default)</Radio>      </RadioGroup>      <RadioGroup orientation="horizontal" label="Position" value={mediaPosition} onChange={setMediaPosition}>        <Radio value="start">Start (Default)</Radio>        <Radio value="end">End</Radio>      </RadioGroup>    </Flex>  );}

Accessibility

Keyboard interaction

KeyAction
TabMoves focus to the next focusable element inside the modal
Shift + TabMoves focus to the previous focusable element inside the modal
EscapeCloses the modal (when dismissable)

ARIA

  • role="dialog" is applied automatically by the underlying React Aria Dialog
  • The title is rendered as an <h2> and linked to the dialog via aria-labelledby
  • The close button is rendered with aria-label="Close"
  • Focus is trapped within the modal while open and returned to the trigger on close

Best Practices

  • Always provide a title so the dialog has an accessible name
  • Pair every footer action with slot="close" when it should dismiss the modal
  • Set isDismissable={false} for required confirmations the user must explicitly answer
  • Provide meaningful alt text for image media, or alt="" if it's purely decorative
  • Prefer the actions prop over manually adding footer buttons in children so they get consistent spacing and alignment

On this page