Dialog
A dialog is a dialog that focuses the user's attention exclusively on an information via a window that is overlaid on primary content.
Import#
import { Dialog } from '@chakra-ui/react'
Usage#
When the dialog opens:
- focus is trapped within the dialog and set to the first tabbable element.
- content behind a dialog dialog is inert, meaning that users cannot interact with it.
function BasicUsage() {const { open, onOpen, onClose } = useDisclosure()return (<><Button onClick={onOpen}>Open Dialog</Button><Dialog.Root open={open} onClose={onClose}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button mr={3} onClick={onClose}>Close</Button><Button variant='ghost'>Secondary Action</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog.Root></>)}
Control Focus when Dialog 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 dialog
closes.
function ReturnFocus() {const { open, 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 Dialog</Button><Dialog.Root finalFocusRef={finalRef} open={open} onClose={onClose}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button mr={3} onClick={onClose}>Close</Button><Button variant='ghost'>Secondary Action</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog.Root></>)}
Block Scrolling when Dialog opens#
For accessibility, it is recommended to block scrolling on the main document
behind the dialog. Chakra does this by default but you can set
blockScrollOnMount
to false
to allow scrolling behind the dialog.
function BasicUsage() {const { open, onOpen, onClose } = useDisclosure()return (<><Button onClick={onOpen}>Open Dialog</Button><Dialog.Root blockScrollOnMount={false} open={open} onClose={onClose}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Text fontWeight='bold' mb='1rem'>You can scroll the content behind the dialog</Text><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button mr={3} onClick={onClose}>Close</Button><Button variant='ghost'>Secondary Action</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog.Root></>)}
Focus on specific element#
Chakra automatically sets focus on the first tabbable element in the dialog. However, there might be scenarios where you need to manually control where focus goes.
Chakra provides 2 props for this use case:
initialFocusRef
: Theref
of the component that receives focus when the dialog opens.finalFocusRef
: Theref
of the component that receives focus when the dialog closes.
If you set
finalFocusRef
, internally we setreturnFocusOnClose
tofalse
so it doesn't return focus to the element that triggered it.
function InitialFocus() {const { open, onOpen, onClose } = useDisclosure()const initialRef = React.useRef(null)const finalRef = React.useRef(null)return (<><Button onClick={onOpen}>Open Dialog</Button><Button ml={4} ref={finalRef}>I'll receive focus on close</Button><DialoginitialFocusRef={initialRef}finalFocusRef={finalRef}open={open}onClose={onClose}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Create your account</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body pb={6}><Field.Root><Field.Label>First name</Field.Label><Input ref={initialRef} placeholder='First name' /></Field.Root><Field.Root mt={4}><Field.Label>Last name</Field.Label><Input placeholder='Last name' /></Field.Root></Dialog.Body><Dialog.Footer><Button mr={3}>Save</Button><Button onClick={onClose}>Cancel</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Close Dialog on Overlay Click#
By default, the dialog closes when you click its overlay. You can set
closeOnOverlayClick
to false
if you want the dialog to stay visible.
function ManualClose() {const { open, onOpen, onClose } = useDisclosure()return (<><Button onClick={onOpen}>Open Dialog</Button><Dialog closeOnOverlayClick={false} open={open} onClose={onClose}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Create your account</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body pb={6}><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button mr={3}>Save</Button><Button onClick={onClose}>Cancel</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Make dialog vertically centered#
By default the dialog has a vertical offset of 3.75rem
which you can change by
passing top
to the Dialog.Content
.
If you need to vertically center the dialog, pass the centered
prop.
If the content within the dialog overflows beyond the viewport, don't use this prop. Try setting the overflow behavior instead.
function VerticallyCenter() {const { open, onOpen, onClose } = useDisclosure()return (<><Button onClick={onOpen}>Trigger dialog</Button><Dialog onClose={onClose} open={open} centered><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button onClick={onClose}>Close</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Changing the transition#
The Dialog
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
.
function TransitionExample() {const { open, onOpen, onClose } = useDisclosure()return (<><Button onClick={onOpen}>Open Dialog</Button><DialogcenteredonClose={onClose}open={open}motionPreset='slideInBottom'><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button mr={3} onClick={onClose}>Close</Button><Button variant='ghost'>Secondary Action</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Changing to alert dialog#
Set the role
prop to alertdialog
to change the dialog to an alert dialog.
Dialog overflow behavior#
If the content within the dialog overflows beyond the viewport, you can use the
scrollBehavior
to control how scrolling should happen.
- If set to
inside
, scroll only occurs within theDialog.Body
. - If set to
outside
, the entireDialog.Content
will scroll within the viewport.
function ScrollingExample() {const { open, onOpen, onClose } = useDisclosure()const [scrollBehavior, setScrollBehavior] = React.useState('inside')const btnRef = React.useRef(null)return (<><RadioGroup.Root value={scrollBehavior} onChange={setScrollBehavior}><Stack direction='row'><RadioGroup.Item value='inside'>inside</RadioGroup.Item><RadioGroup.Item value='outside'>outside</RadioGroup.Item></Stack></RadioGroup.Root><Button mt={3} ref={btnRef} onClick={onOpen}>Trigger dialog</Button><DialogonClose={onClose}finalFocusRef={btnRef}open={open}scrollBehavior={scrollBehavior}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Lorem count={15} /></Dialog.Body><Dialog.Footer><Button onClick={onClose}>Close</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Dialog Sizes#
Pass the size
prop if you need to adjust the size of the dialog. Values can be
xs
, sm
, md
, lg
, xl
, or full
.
function SizeExample() {const { open, 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) => (<ButtononClick={() => handleSizeClick(size)}key={size}m={4}>{`Open ${size} Dialog`}</Button>))}<Dialog onClose={onClose} size={size} open={open}><Dialog.Backdrop /><Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Lorem count={2} /></Dialog.Body><Dialog.Footer><Button onClick={onClose}>Close</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Making other elements Inert#
When the dialog 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
dialog. To disable this behavior, set useInert
to false
.
Prevent focus trapping#
By default the dialog, 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 dialog 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 CSS property, the example below included.
The filter
property will have no effect on the background because the Dialog
is rendered within a Portal
. This means you can only style components within
the Dialog by using this property.
function BackdropExample() {const OverlayOne = () => (<Dialog.Backdropbg='blackAlpha.300'backdropFilter='blur(10px) hue-rotate(90deg)'/>)const OverlayTwo = () => (<Dialog.Backdropbg='none'backdropFilter='auto'backdropInvert='80%'backdropBlur='2px'/>)const { open, onOpen, onClose } = useDisclosure()const [overlay, setOverlay] = React.useState(<OverlayOne />)return (<><ButtononClick={() => {setOverlay(<OverlayOne />)onOpen()}}>Use Overlay one</Button><Buttonml='4'onClick={() => {setOverlay(<OverlayTwo />)onOpen()}}>Use Overlay two</Button><Dialog centered open={open} onClose={onClose}>{overlay}<Dialog.Positioner><Dialog.Content><Dialog.Header>Dialog Title</Dialog.Header><Dialog.CloseTrigger /><Dialog.Body><Text>Custom backdrop filters!</Text></Dialog.Body><Dialog.Footer><Button onClick={onClose}>Close</Button></Dialog.Footer></Dialog.Content></Dialog.Positioner></Dialog></>)}
Accessibility#
Keyboard and Focus Management#
- When the dialog opens, focus is trapped within it.
- When the dialog opens, focus is automatically set to the first enabled
element, or the element from
initialFocusRef
. - When the dialog closes, focus returns to the element that was focused before
the dialog activated, or the element from
finalFocusRef
. - Clicking on the overlay closes the Dialog.
- Pressing Esc closes the Dialog.
- Scrolling is blocked on the elements behind the dialog.
- The dialog 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 addaria-hidden
to its siblings.
ARIA#
- The
Dialog.Content
hasaria-dialog
set totrue
. - The
Dialog.Content
hasaria-labelledby
set to theid
of theDialog.Header
. - The
Dialog.Content
hasaria-describedby
set to theid
of theDialog.Body
.
Props#
Modal Props#
Other components#
ModalOverlay
,ModalHeader
,ModalFooter
andModalBody
composesBox
component.ModalCloseButton
composesCloseButton
.
Theming#
The Modal
component is a multipart component.
To learn more about styling multipart components, visit the Component Style page.
Anatomy#
- A:
header
- B:
overlay
- C:
dialogContainer
- D:
dialog
- E:
closeButton
- F:
body
- G:
footer
You can find more information in the source here.
Theming properties#
The properties that affect the theming of the Modal
component are:
variant
: The visual variant of theModal
. Defaults to baseStyle.size
: The size of theModal
. Defaults to md.
Theming utilities#
createMultiStyleConfigHelpers
: a function that returns a set of utilities for creating style configs for a multipart component (definePartsStyle and defineMultiStyleConfig).definePartsStyle
: a function used to create multipart style objects.defineMultiStyleConfig
: a function used to define the style configuration for a multipart component.
Customizing the default theme#
import { modalAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)const baseStyle = definePartsStyle({// define the part you're going to styleoverlay: {bg: 'blackAlpha.200', //change the background},dialog: {borderRadius: 'md',bg: `purple.100`,},})export const modalTheme = defineMultiStyleConfig({baseStyle,})
After customizing the modal theme, we can import it in our theme file and add it in the components property:
import { extendTheme } from '@chakra-ui/react'import { modalTheme } from './components/theme/modal'export const theme = extendTheme({components: { Modal: modalTheme },})
This is a crucial step to make sure that any changes that we make to the
Modal
theme are applied.
Adding a custom size#
Let's assume we want to change the font size of both header and dialog.
import { modalAnatomy as parts } from '@chakra-ui/anatomy'import {createMultiStyleConfigHelpers,defineStyle,} from '@chakra-ui/styled-system'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)const xl = defineStyle({px: '6',py: '2',fontSize: 'xl',})const sm = defineStyle({fontSize: 'sm',py: '6',})const sizes = {xl: definePartsStyle({ header: sm, dialog: xl }),}export const modalTheme = defineMultiStyleConfig({sizes,})// Now we can use the new `xl` size<Modal size="xl" ... />
Every time you're adding anything new to the theme, you'd need to run the CLI command to get proper autocomplete in your IDE. You can learn more about the CLI tool here.
Adding a custom variant#
Let's assume we want to include a custom variant. Here's how we can do that:
import { modalAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)const purple = definePartsStyle({dialog: {borderRadius: 'md',bg: `purple.100`,// Let's also provide dark mode alternatives_dark: {bg: `purple.600`,color: 'white',},},})export const modalTheme = defineMultiStyleConfig({variants: { purple },})// Now we can use the new `purple` variant<Modal variant='purple' ... />
Changing the default properties#
Let's assume we want to change the default size and variant of every Modal
in
our app.
import { modalAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system'const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(parts.keys)export const modalTheme = defineMultiStyleConfig({defaultProps: {size: 'xl',variant: 'purple',},})// This saves you time, instead of manually setting the size and variant every time you use an Modal:<Modal size="xl" variant="purple" ... />
Showcase#
import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalFooter, ModalBody, ModalCloseButton, useDisclosure, Button, Box, IconButton, useColorMode, } from "@chakra-ui/react"; import { FaMoon, FaSun } from "react-icons/fa"; export default function App() { const { isOpen, onOpen, onClose } = useDisclosure(); const { toggleColorMode, colorMode } = useColorMode(); return ( <Box position="relative" h="100vh" p={12}> <Button onClick={onOpen}>Open Modal</Button> <Modal isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>Modal Title</ModalHeader> <ModalCloseButton /> <ModalBody> Lorem ipsum dolor sit amet. Et corporis quisquam eum adipisci impedit quo eius nisi est aspernatur vel veniam velit qui numquam totam. Vel debitis sint ut culpa cupiditate a dolores voluptates ut vero voluptatem non rerum aliquid qui sapiente possimus. Eum natus voluptates hic galisum architecto et nobis incidunt ut odio ipsum qui repudiandae voluptatem. </ModalBody> <ModalFooter> <Button colorScheme="blue" mr={3} onClick={onClose}> Close </Button> <Button variant="ghost">Secondary Action</Button> </ModalFooter> </ModalContent> </Modal> <IconButton aria-label="change theme" rounded="full" size="xs" position="absolute" bottom={4} left={4} onClick={toggleColorMode} icon={colorMode === "dark" ? <FaSun /> : <FaMoon />} /> </Box> ); }