---
id: modal
category: overlay
title: Modal
package: '@chakra-ui/modal'
description:
  A modal is a dialog that focuses the user's attention exclusively on an
  information via a window that is overlaid on primary content.
---

## Import

Chakra exports 7 components to help you create any modal dialog.

- `Modal`: The wrapper that provides context for its children.
- `ModalOverlay`: The dimmed overlay behind the modal dialog.
- `ModalContent`: The container for the modal dialog's content.
- `ModalHeader`: The header that labels the modal dialog.
- `ModalFooter`: The footer that houses the modal actions.
- `ModalBody`: The wrapper that houses the modal's main content.
- `ModalCloseButton`: The button that closes the modal.

```js
import {
  Modal,
  ModalOverlay,
  ModalContent,
  ModalHeader,
  ModalFooter,
  ModalBody,
  ModalCloseButton,
} from '@chakra-ui/react'
```

## Usage

When the modal opens:

- focus is trapped within the modal and set to the first tabbable element.
- content behind a modal dialog is **inert**, meaning that users cannot interact
  with it.

```jsx
function BasicUsage() {
  const { isOpen, onOpen, onClose } = useDisclosure()
  return (
    <>
      <Button onClick={onOpen}>Open Modal</Button>

      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={2} />
          </ModalBody>

          <ModalFooter>
            <Button colorScheme='blue' mr={3} onClick={onClose}>
              Close
            </Button>
            <Button variant='ghost'>Secondary Action</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Control Focus when Modal closes

When the dialog closes, it returns focus to the element that triggered it. Set
`finalFocusRef` to change the element that should receive focus when the modal
closes.

```jsx
function ReturnFocus() {
  const { isOpen, onOpen, onClose } = useDisclosure()
  const finalRef = React.useRef(null)

  return (
    <>
      <Box ref={finalRef} tabIndex={-1} aria-label='Focus moved to this box'>
        Some other content that'll receive focus on close.
      </Box>

      <Button mt={4} onClick={onOpen}>
        Open Modal
      </Button>
      <Modal finalFocusRef={finalRef} isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={2} />
          </ModalBody>

          <ModalFooter>
            <Button colorScheme='blue' mr={3} onClick={onClose}>
              Close
            </Button>
            <Button variant='ghost'>Secondary Action</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Block Scrolling when Modal opens

For accessibility, it is recommended to block scrolling on the main document
behind the modal. Chakra does this by default but you can set
`blockScrollOnMount` to `false` to allow scrolling behind the modal.

```jsx
function BasicUsage() {
  const { isOpen, onOpen, onClose } = useDisclosure()

  return (
    <>
      <Button onClick={onOpen}>Open Modal</Button>

      <Modal blockScrollOnMount={false} isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Text fontWeight='bold' mb='1rem'>
              You can scroll the content behind the modal
            </Text>
            <Lorem count={2} />
          </ModalBody>

          <ModalFooter>
            <Button colorScheme='blue' mr={3} onClick={onClose}>
              Close
            </Button>
            <Button variant='ghost'>Secondary Action</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Focus on specific element

Chakra automatically sets focus on the first tabbable element in the modal.
However, there might be scenarios where you need to manually control where focus
goes.

Chakra provides 2 props for this use case:

- `initialFocusRef`: The `ref` of the component that receives focus when the
  modal opens.
- `finalFocusRef`: The `ref` of the component that receives focus when the modal
  closes.

> If you set `finalFocusRef`, internally we set `returnFocusOnClose` to `false`
> so it doesn't return focus to the element that triggered it.

```jsx
function InitialFocus() {
  const { isOpen, onOpen, onClose } = useDisclosure()

  const initialRef = React.useRef(null)
  const finalRef = React.useRef(null)

  return (
    <>
      <Button onClick={onOpen}>Open Modal</Button>
      <Button ml={4} ref={finalRef}>
        I'll receive focus on close
      </Button>

      <Modal
        initialFocusRef={initialRef}
        finalFocusRef={finalRef}
        isOpen={isOpen}
        onClose={onClose}
      >
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Create your account</ModalHeader>
          <ModalCloseButton />
          <ModalBody pb={6}>
            <FormControl>
              <FormLabel>First name</FormLabel>
              <Input ref={initialRef} placeholder='First name' />
            </FormControl>

            <FormControl mt={4}>
              <FormLabel>Last name</FormLabel>
              <Input placeholder='Last name' />
            </FormControl>
          </ModalBody>

          <ModalFooter>
            <Button colorScheme='blue' mr={3}>
              Save
            </Button>
            <Button onClick={onClose}>Cancel</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Close Modal on Overlay Click

By default, the modal closes when you click its overlay. You can set
`closeOnOverlayClick` to `false` if you want the modal to stay visible.

```jsx
function ManualClose() {
  const { isOpen, onOpen, onClose } = useDisclosure()

  return (
    <>
      <Button onClick={onOpen}>Open Modal</Button>

      <Modal closeOnOverlayClick={false} isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Create your account</ModalHeader>
          <ModalCloseButton />
          <ModalBody pb={6}>
            <Lorem count={2} />
          </ModalBody>

          <ModalFooter>
            <Button colorScheme='blue' mr={3}>
              Save
            </Button>
            <Button onClick={onClose}>Cancel</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Make modal vertically centered

By default the modal has a vertical offset of `3.75rem` which you can change by
passing `top` to the `ModalContent`. If you need to vertically center the modal,
pass the `isCentered` prop.

> If the content within the modal overflows beyond the viewport, don't use this
> prop. Try setting the [overflow behavior](#modal-overflow-behavior) instead.

```jsx
function VerticallyCenter() {
  const { isOpen, onOpen, onClose } = useDisclosure()

  return (
    <>
      <Button onClick={onOpen}>Trigger modal</Button>

      <Modal onClose={onClose} isOpen={isOpen} isCentered>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={2} />
          </ModalBody>
          <ModalFooter>
            <Button onClick={onClose}>Close</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Changing the transition

The `Modal` comes with a scale transition by default but you can change it by
passing a `motionPreset` prop, and setting its value to either `slideInBottom`,
`slideInRight`, `scale` or `none`.

```jsx
function TransitionExample() {
  const { isOpen, onOpen, onClose } = useDisclosure()
  return (
    <>
      <Button onClick={onOpen}>Open Modal</Button>
      <Modal
        isCentered
        onClose={onClose}
        isOpen={isOpen}
        motionPreset='slideInBottom'
      >
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={2} />
          </ModalBody>
          <ModalFooter>
            <Button colorScheme='blue' mr={3} onClick={onClose}>
              Close
            </Button>
            <Button variant='ghost'>Secondary Action</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Modal overflow behavior

If the content within the modal overflows beyond the viewport, you can use the
`scrollBehavior` to control how scrolling should happen.

- If set to `inside`, scroll only occurs within the `ModalBody`.
- If set to `outside`, the entire `ModalContent` will scroll within the
  viewport.

```jsx
function ScrollingExample() {
  const { isOpen, onOpen, onClose } = useDisclosure()
  const [scrollBehavior, setScrollBehavior] = React.useState('inside')

  const btnRef = React.useRef(null)
  return (
    <>
      <RadioGroup value={scrollBehavior} onChange={setScrollBehavior}>
        <Stack direction='row'>
          <Radio value='inside'>inside</Radio>
          <Radio value='outside'>outside</Radio>
        </Stack>
      </RadioGroup>

      <Button mt={3} ref={btnRef} onClick={onOpen}>
        Trigger modal
      </Button>

      <Modal
        onClose={onClose}
        finalFocusRef={btnRef}
        isOpen={isOpen}
        scrollBehavior={scrollBehavior}
      >
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={15} />
          </ModalBody>
          <ModalFooter>
            <Button onClick={onClose}>Close</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Modal Sizes

Pass the `size` prop if you need to adjust the size of the modal. Values can be
`xs`, `sm`, `md`, `lg`, `xl`, or `full`.

```jsx
function SizeExample() {
  const { isOpen, onOpen, onClose } = useDisclosure()
  const [size, setSize] = React.useState('md')

  const handleSizeClick = (newSize) => {
    setSize(newSize)
    onOpen()
  }

  const sizes = ['xs', 'sm', 'md', 'lg', 'xl', 'full']

  return (
    <>
      {sizes.map((size) => (
        <Button
          onClick={() => handleSizeClick(size)}
          key={size}
          m={4}
        >{`Open ${size} Modal`}</Button>
      ))}

      <Modal onClose={onClose} size={size} isOpen={isOpen}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={2} />
          </ModalBody>
          <ModalFooter>
            <Button onClick={onClose}>Close</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

### Making other elements Inert

When the modal is open, it is rendered within a portal and all its siblings have
`aria-hidden` set to `true` so the only thing screen readers see is the modal.
To disable this behavior, set `useInert` to `false`.

### Prevent focus trapping

By default the modal, alert dialog and drawer locks the focus inside them.
Normally this is what you want to maintain accessibility standards.

**While we strongly discourage this use case** due to the accessibility impacts,
there are certain situations where you might not want the modal to trap focus.

To prevent focus trapping, pass `trapFocus` and set its value to `false`.

### Styling the backdrop

The backdrop's background by default is set to `blackAlpha.600`, but if you want
to achieve a different style you can also use the backdrop style props, like
`backdropBlur`, `backdropBrightness`, `backdropContrast`, `backdropHueRotate`,
`backdropInvert`, and `backdropSaturate`. To use these style props, you'd have
to set the `backdropFilter` prop to `auto`.

> Please be aware that not every browser supports the
> [backdrop-filter](https://caniuse.com/css-backdrop-filter) CSS property, the
> example below included.

The `filter` property will have no effect on the background because the Modal is
rendered within a `Portal`. This means you can only style components within the
Modal by using this property.

```jsx
function BackdropExample() {
  const OverlayOne = () => (
    <ModalOverlay
      bg='blackAlpha.300'
      backdropFilter='blur(10px) hue-rotate(90deg)'
    />
  )

  const OverlayTwo = () => (
    <ModalOverlay
      bg='none'
      backdropFilter='auto'
      backdropInvert='80%'
      backdropBlur='2px'
    />
  )

  const { isOpen, onOpen, onClose } = useDisclosure()
  const [overlay, setOverlay] = React.useState(<OverlayOne />)

  return (
    <>
      <Button
        onClick={() => {
          setOverlay(<OverlayOne />)
          onOpen()
        }}
      >
        Use Overlay one
      </Button>
      <Button
        ml='4'
        onClick={() => {
          setOverlay(<OverlayTwo />)
          onOpen()
        }}
      >
        Use Overlay two
      </Button>
      <Modal isCentered isOpen={isOpen} onClose={onClose}>
        {overlay}
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Text>Custom backdrop filters!</Text>
          </ModalBody>
          <ModalFooter>
            <Button onClick={onClose}>Close</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
```

## Accessibility

### Keyboard and Focus Management

- When the modal opens, focus is trapped within it.
- When the modal opens, focus is automatically set to the first enabled element,
  or the element from `initialFocusRef`.
- When the modal closes, focus returns to the element that was focused before
  the modal activated, or the element from `finalFocusRef`.
- Clicking on the overlay closes the Modal.
- Pressing <kbd>Esc</kbd> closes the Modal.
- Scrolling is blocked on the elements behind the modal.
- The modal is rendered in a portal attached to the end of `document.body` to
  break it out of the source order and make it easy to add `aria-hidden` to its
  siblings.

### ARIA

- The `ModalContent` has `aria-modal` set to `true`.
- The `ModalContent` has `aria-labelledby` set to the `id` of the `ModalHeader`.
- The `ModalContent` has `aria-describedby` set to the `id` of the `ModalBody`.
