# Modal

**📖 Live documentation:** https://cds.coinbase.com/components/overlay/Modal/?platform=mobile

A component that displays content in a window that requires user interaction.

## Import

```tsx
import { Modal } from '@coinbase/cds-mobile/overlays/modal/Modal'
```

## Examples

### Basic example

```jsx
function Example() {
  const [visible, setVisible] = useState(false);
  return (
    <>
      <Button onPress={() => setVisible(true)}>Open Modal</Button>
      <Modal onRequestClose={() => setVisible(false)} visible={visible}>
        <ModalHeader
          backAccessibilityLabel="Back"
          closeAccessibilityLabel="Close"
          onBackButtonClick={() => setVisible(false)}
          testID="Basic Modal Test ID"
          title="Basic Modal"
        />
        <ModalBody tabIndex={0} testID="modal-body">
          Body contents go here
        </ModalBody>
        <ModalFooter
          primaryAction={<Button onPress={() => setVisible(false)}>Save</Button>}
          secondaryAction={
            <Button onPress={() => setVisible(false)} variant="secondary">
              Cancel
            </Button>
          }
        />
      </Modal>
    </>
  );
}
```

### Using with react-navigation

When a screen is presented as a modal via react-navigation (`presentation: "fullScreenModal"`),
the navigator owns the overlay, transition animation, and swipe-to-dismiss gesture. Use
`ModalContext` to wire up `ModalHeader`, `ModalBody`, and `ModalFooter` directly inside
your screen component — no `Modal` wrapper needed.

Use `useSafeAreaInsets` to read the top inset and apply it as `paddingTop` on the root view
so the header is pushed below the status bar and Dynamic Island.

**Imports**

```jsx
import { ModalHeader, ModalBody, ModalFooter } from '@coinbase/cds-mobile';
import { ModalContext } from '@coinbase/cds-common/overlays/ModalContext';
import { VStack } from '@coinbase/cds-mobile/layout';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
```

**Navigator configuration**

```jsx
// Stack navigator configuration:
<Stack.Screen
  name="Settings"
  component={SettingsScreen}
  options={{ presentation: 'fullScreenModal' }}
/>
```

**Screen component**

```jsx
function SettingsScreen({ navigation }) {
  const handleClose = useCallback(() => navigation.goBack(), [navigation]);
  const { top } = useSafeAreaInsets();

  const modalContext = useMemo(
    () => ({
      visible: true,
      onRequestClose: handleClose,
    }),
    [handleClose],
  );

  const rootStyle = useMemo(() => ({ paddingTop: top }), [top]);

  return (
    <ModalContext.Provider value={modalContext}>
      <VStack flexGrow={1} style={rootStyle}>
        <ModalHeader closeAccessibilityLabel="Close" title="Settings" />
        <ModalBody>{/* screen content */}</ModalBody>
        <ModalFooter primaryAction={<Button onPress={handleClose}>Save</Button>} />
      </VStack>
    </ModalContext.Provider>
  );
}
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `onRequestClose` | `() => void` | Yes | `-` | Callback function fired when modal is closed. |
| `visible` | `boolean` | Yes | `false` | Controls visibility of the Modal |
| `accessibilityLabelledBy` | `string \| (string & string[])` | No | `-` | Identifies the element that labels the element it is applied to. When the assistive technology focuses on the component with this props, the text is read aloud. The value should should match the nativeID of the related element. |
| `allowSwipeDismissal` | `boolean` | No | `-` | Controls whether the modal can be dismissed by swiping down on iOS. This requires you to implement the onRequestClose prop to handle the dismissal. |
| `animated` | `boolean` | No | `-` | - |
| `backdropColor` | `string \| OpaqueColorValue` | No | `-` | The backdropColor props sets the background color of the modals container. Defaults to white if not provided and transparent is false. Ignored if transparent is true. |
| `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
| `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderBottomWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderColor` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
| `borderEndWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderStartWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderTopLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderTopRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderTopWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `bordered` | `boolean` | No | `-` | Add a border around all sides of the box. |
| `borderedBottom` | `boolean` | No | `-` | Add a border to the bottom side of the box. |
| `borderedEnd` | `boolean` | No | `-` | Add a border to the trailing side of the box. |
| `borderedHorizontal` | `boolean` | No | `-` | Add a border to the leading and trailing sides of the box. |
| `borderedStart` | `boolean` | No | `-` | Add a border to the leading side of the box. |
| `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
| `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
| `children` | `ReactNode \| ((props: ModalChildrenRenderProps) => ReactNode)` | No | `-` | Component to render as the Modal content |
| `color` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
| `elevation` | `0 \| 1 \| 2` | No | `-` | Determines box shadow styles. Parent should have overflow set to visible to ensure styles are not clipped. |
| `hardwareAccelerated` | `boolean` | No | `-` | Controls whether to force hardware acceleration for the underlying window. |
| `hideCloseButton` | `boolean` | No | `false` | Hide the close icon on the top right |
| `hideDividers` | `boolean` | No | `false` | Hide top and bottom dividers inside Modal body |
| `key` | `Key \| null` | No | `-` | - |
| `maxHeight` | `null \| number \| AnimatedNode \| auto \| ${number}%` | No | `-` | - |
| `maxWidth` | `null \| number \| AnimatedNode \| auto \| ${number}%` | No | `-` | - |
| `minHeight` | `null \| number \| AnimatedNode \| auto \| ${number}%` | No | `-` | - |
| `minWidth` | `null \| number \| AnimatedNode \| auto \| ${number}%` | No | `-` | - |
| `navigationBarTranslucent` | `boolean` | No | `-` | Determines whether your modal should go under the system navigationbar. |
| `onDidClose` | `(() => void)` | No | `-` | Callback fired after the component is closed. |
| `onDismiss` | `(() => void)` | No | `-` | The onDismiss prop allows passing a function that will be called once the modal has been dismissed. |
| `onOrientationChange` | `((event: NativeSyntheticEvent<any>) => void)` | No | `-` | The onOrientationChange callback is called when the orientation changes while the modal is being displayed. The orientation provided is only portrait or landscape. This callback is also called on initial render, regardless of the current orientation. |
| `onPointerCancel` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerCancelCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerDown` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerDownCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerEnter` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerEnterCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerLeave` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerLeaveCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerMove` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerMoveCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerUp` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onPointerUpCapture` | `((event: PointerEvent) => void)` | No | `-` | - |
| `onShow` | `((event: NativeSyntheticEvent<any>) => void)` | No | `-` | The onShow prop allows passing a function that will be called once the modal has been shown. |
| `padding` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `paddingBottom` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `paddingEnd` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `paddingStart` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `paddingTop` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `paddingX` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `paddingY` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1.5` | No | `-` | - |
| `presentationStyle` | `fullScreen \| pageSheet \| formSheet \| overFullScreen` | No | `-` | The presentationStyle determines the style of modal to show |
| `ref` | `null \| RefObject<View \| null> \| (instance: View \| null) => void \| (() => VoidOrUndefinedOnly)` | No | `-` | Allows getting a ref to the component instance. Once the component unmounts, React will set ref.current to null (or call the ref with null if you passed a callback ref). |
| `statusBarTranslucent` | `boolean` | No | `-` | Determines whether your modal should go under the system statusbar. |
| `styles` | `{ modal?: StyleProp<ViewStyle>; safeArea?: StyleProp<ViewStyle>; }` | No | `-` | Custom styles for individual elements of the Modal |
| `supportedOrientations` | `(portrait \| portrait-upside-down \| landscape \| landscape-left \| landscape-right)[]` | No | `-` | The supportedOrientations prop allows the modal to be rotated to any of the specified orientations. On iOS, the modal is still restricted by whats specified in your apps Info.plists UISupportedInterfaceOrientations field. |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID Used to locate this view in end-to-end tests. |
| `transparent` | `boolean` | No | `-` | The transparent prop determines whether your modal will fill the entire view. Setting this to true will render the modal over a transparent background. |
| `width` | `number` | No | `-` | - |
| `zIndex` | `number` | No | `-` | - |


## Styles

| Selector | Static class name | Description |
| --- | --- | --- |
| `modal` | `-` | Visible modal card element |
| `safeArea` | `-` | Safe area region wrapping the modal children |


