The padding required to prevent the arrow from reaching the very edge of the popper.
Popover
Popover is a non-modal dialog that floats around a trigger.
Popover is built on top of the Popper.js library.
Import#
Popover: The wrapper that provides props, state, and context to its children.PopoverTrigger: Used to wrap the reference (or trigger) element.PopoverContent: The popover itself.PopoverHeader: The header of the popover.PopoverBody: The body of the popover.PopoverArrow: A visual arrow that points to the reference (or trigger).PopoverCloseButton: A button to close the popover.PopoverAnchor: Used to wrap the position-reference element.
import {Popover,PopoverTrigger,PopoverContent,PopoverHeader,PopoverBody,PopoverFooter,PopoverArrow,PopoverCloseButton,PopoverAnchor,} from '@chakra-ui/react'
Basic Usage#
It is used to display contextual information to the user, and should be paired with a clickable trigger element.
When Popover opens, focus is sent to PopoverContent. When it closes, focus is
returned to the trigger.
Tip: When using this component, ensure the children passed to
PopoverTriggeris focusable and has a forwardedref.
<Popover><PopoverTrigger><Button>Trigger</Button></PopoverTrigger><PopoverContent><PopoverArrow /><PopoverCloseButton /><PopoverHeader>Confirmation!</PopoverHeader><PopoverBody>Are you sure you want to have that milkshake?</PopoverBody></PopoverContent></Popover>
Rendering the Popover in a Portal#
By default, the Popover doesn't render in a Portal. To make them display in a
portal, wrap the PopoverContent in a Portal
You might need to Inspect Element to see this in action. Notice that
PopoverContentis rendered as a child of<body>
<Popover><PopoverTrigger><Button>Trigger</Button></PopoverTrigger><Portal><PopoverContent><PopoverArrow /><PopoverHeader>Header</PopoverHeader><PopoverCloseButton /><PopoverBody><Button colorScheme='blue'>Button</Button></PopoverBody><PopoverFooter>This is the footer</PopoverFooter></PopoverContent></Portal></Popover>
Focus an element when Popover opens#
By default, focus is to sent to PopoverContent when it opens. Pass the
initialFocusRef prop to send focus to a specific element instead.
Focusing a child element only works for popovers with a
clicktrigger, nothover.
function WalkthroughPopover() {const initialFocusRef = React.useRef()return (<PopoverinitialFocusRef={initialFocusRef}placement='bottom'closeOnBlur={false}><PopoverTrigger><Button>Trigger</Button></PopoverTrigger><PopoverContent color='white' bg='blue.800' borderColor='blue.800'><PopoverHeader pt={4} fontWeight='bold' border='0'>Manage Your Channels</PopoverHeader><PopoverArrow /><PopoverCloseButton /><PopoverBody>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed doeiusmod tempor incididunt ut labore et dolore.</PopoverBody><PopoverFooterborder='0'display='flex'alignItems='center'justifyContent='space-between'pb={4}><Box fontSize='sm'>Step 2 of 4</Box><ButtonGroup size='sm'><Button colorScheme='green'>Setup Email</Button><Button colorScheme='blue' ref={initialFocusRef}>Next</Button></ButtonGroup></PopoverFooter></PopoverContent></Popover>)}
Trapping Focus within Popover#
If the popover contains a form, you might need to trap focus within the popover and close it when the user fills the form and hits "save".
You can leverage
react-focus-lock to trap focus
within the PopoverContent.
// import FocusLock from "react-focus-lock"// 1. Create a text input componentconst TextInput = React.forwardRef((props, ref) => {return (<FormControl><FormLabel htmlFor={props.id}>{props.label}</FormLabel><Input ref={ref} id={props.id} {...props} /></FormControl>)})// 2. Create the formconst Form = ({ firstFieldRef, onCancel }) => {return (<Stack spacing={4}><TextInputlabel='First name'id='first-name'ref={firstFieldRef}defaultValue='John'/><TextInput label='Last name' id='last-name' defaultValue='Smith' /><ButtonGroup display='flex' justifyContent='flex-end'><Button variant='outline' onClick={onCancel}>Cancel</Button><Button isDisabled colorScheme='teal'>Save</Button></ButtonGroup></Stack>)}// 3. Create the Popover// Ensure you set `closeOnBlur` prop to false so it doesn't close on outside clickconst PopoverForm = () => {const { onOpen, onClose, isOpen } = useDisclosure()const firstFieldRef = React.useRef(null)return (<><Box display='inline-block' mr={3}>John Smith</Box><PopoverisOpen={isOpen}initialFocusRef={firstFieldRef}onOpen={onOpen}onClose={onClose}placement='right'closeOnBlur={false}><PopoverTrigger><IconButton size='sm' icon={<EditIcon />} /></PopoverTrigger><PopoverContent p={5}><FocusLock returnFocus persistentFocus={false}><PopoverArrow /><PopoverCloseButton /><Form firstFieldRef={firstFieldRef} onCancel={onClose} /></FocusLock></PopoverContent></Popover></>)}render(<PopoverForm />)
Controlled Usage#
You can control the opening and closing of the popover by passing the isOpen,
and onClose props.
Sometimes you might need to set the returnFocusOnClose prop to false to
prevent popover from returning focus to PopoverTrigger's children.
function ControlledUsage() {const { isOpen, onToggle, onClose } = useDisclosure()return (<><Button mr={5} onClick={onToggle}>Trigger</Button><PopoverreturnFocusOnClose={false}isOpen={isOpen}onClose={onClose}placement='right'closeOnBlur={false}><PopoverTrigger><Button colorScheme='pink'>Popover Target</Button></PopoverTrigger><PopoverContent><PopoverHeader fontWeight='semibold'>Confirmation</PopoverHeader><PopoverArrow /><PopoverCloseButton /><PopoverBody>Are you sure you want to continue with your action?</PopoverBody><PopoverFooter display='flex' justifyContent='flex-end'><ButtonGroup size='sm'><Button variant='outline'>Cancel</Button><Button colorScheme='red'>Apply</Button></ButtonGroup></PopoverFooter></PopoverContent></Popover></>)}
Popover Anchor#
You can wrap your component with PopoverAnchor to prevent trigger any action.
The wrapped component will become a position reference. Actions will only be
triggered by components inside PopoverTrigger.
In this case, you can only open and close the popover with Button. If you
click on Input, it acts same as the original input and doesn't trigger any
action about popover.
🚨 Note: As with PopoverTrigger, if you use a custom component inside
PopoverAnchorensure that it uses Chakra's forwardRef so the popover is triggered successfully.
function WithPopoverAnchor() {const [isEditing, setIsEditing] = useBoolean()const [color, setColor] = React.useState('red')return (<PopoverisOpen={isEditing}onOpen={setIsEditing.on}onClose={setIsEditing.off}closeOnBlur={false}isLazylazyBehavior='keepMounted'><HStack><PopoverAnchor><Inputcolor={color}w='auto'display='inline-flex'isDisabled={!isEditing}defaultValue='Popover Anchor'/></PopoverAnchor><PopoverTrigger><Button h='40px' colorScheme='pink'>{isEditing ? 'Save' : 'Edit'}</Button></PopoverTrigger></HStack><PopoverContent><PopoverBody>Colors:<RadioGroup value={color} onChange={(newColor) => setColor(newColor)}><Radio value='red'>red</Radio><Radio value='blue'>blue</Radio><Radio value='green'>green</Radio><Radio value='purple'>purple</Radio></RadioGroup></PopoverBody></PopoverContent></Popover>)}
Accessing Internal state#
Chakra provides access to two internal details: isOpen and onClose. Use the
render prop pattern to gain access to them.
function InternalStateEx() {const initRef = React.useRef()return (<Popover closeOnBlur={false} placement='left' initialFocusRef={initRef}>{({ isOpen, onClose }) => (<><PopoverTrigger><Button>Click to {isOpen ? 'close' : 'open'}</Button></PopoverTrigger><Portal><PopoverContent><PopoverHeader>This is the header</PopoverHeader><PopoverCloseButton /><PopoverBody><Box>Hello. Nice to meet you! This is the body of the popover</Box><Buttonmt={4}colorScheme='blue'onClick={onClose}ref={initRef}>Close</Button></PopoverBody><PopoverFooter>This is the footer</PopoverFooter></PopoverContent></Portal></>)}</Popover>)}
Customizing the Popover#
Chakra exports all the components you need to customize the look and feel of the popover. You can change the background, arrow size, box shadow and so on.
<Popover><PopoverTrigger><BoxtabIndex='0'role='button'aria-label='Some box'p={5}w='120px'bg='gray.300'children='Click'/></PopoverTrigger><PopoverContent bg='tomato' color='white'><PopoverHeader fontWeight='semibold'>Customization</PopoverHeader><PopoverArrow bg='pink.500' /><PopoverCloseButton bg='purple.500' /><PopoverBody>Tadaa!! The arrow color and background color is customized. Check theprops for each component.</PopoverBody></PopoverContent></Popover>
Popover Placements#
Since popover is powered by PopperJS, you can change the placement of the
popover by passing the placement prop.
The possible values are:
bottom-start |
| | bottom (default) |
| | bottom-end |
auto-start |
| | auto |
| | auto-end |
top-start |
| | top |
| | top-end |
left-start |
| | left |
| | left-end |
right-start |
| | right |
| | right-end |
Even though you specified the placement, Popover will try to reposition itself in the event that available space at the specified placement isn't enough.
<Popover placement='top-start'><PopoverTrigger><Button>Click me</Button></PopoverTrigger><PopoverContent><PopoverHeader fontWeight='semibold'>Popover placement</PopoverHeader><PopoverArrow /><PopoverCloseButton /><PopoverBody>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmodtempor incididunt ut labore et dolore.</PopoverBody></PopoverContent></Popover>
Lazily mounting Popover#
By default, the Popover component renders children of PopoverContent to the
DOM, meaning that invisible popover contents are still rendered but are hidden
by styles.
If you want to defer rendering of popover content until that Popover is
opened, you can use the isLazy prop. This is useful if your PopoverContent
needs to be extra performant, or make network calls on mount that should only
happen when the component is displayed.
<Popover isLazy><PopoverTrigger><Button>Click me</Button></PopoverTrigger><PopoverContent><PopoverHeader fontWeight='semibold'>Popover placement</PopoverHeader><PopoverArrow /><PopoverCloseButton /><PopoverBody>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmodtempor incididunt ut labore et dolore.</PopoverBody></PopoverContent></Popover>
Accessibility#
When you see the word "trigger", it is referring to the
childrenofPopoverTrigger
Keyboard and Focus#
- When the popover is opened, focus is moved to the
PopoverContent. If theinitialFocusRefis set, then focus moves to the element with thatref. - When the popover is closed, focus returns to the trigger. If you set
returnFocusOnClosetofalse, focus will not return. - If trigger is set to
hover:- Focusing on or mousing over the trigger will open the popover.
- Blurring or mousing out of the trigger will close the popover. If you move
your mouse into the
PopoverContent, it'll remain visible.
- If trigger is set to
click:- Clicking the trigger or using the
SpaceorEnterwhen focus is on the trigger will open the popover. - Clicking the trigger again will close the popover.
- Clicking the trigger or using the
- Hitting the
Esckey while the popover is open and focus is within thePopoverContent, will close the popover. If you setcloseOnEsctofalse, it will not close. - Clicking outside or blurring out of the
PopoverContentcloses the popover. If you setcloseOnBlurtofalse, it will not close.
ARIA Attributes#
- If the trigger is set to
click, thePopoverContentelement has role set todialog. If the trigger is set tohover, thePopoverContenthasroleset totooltip. - The
PopoverContenthasaria-labelledbyset to theidof thePopoverHeader. - The
PopoverContenthasaria-describedbyset to theidof thePopoverBody. - The
PopoverContenthasaria-hiddenset totrueorfalsedepending on the open/closed state of the popover. - The trigger has
aria-haspopupset totrueto denote that it triggers a popover. - The trigger has
aria-controlsset to theidof thePopoverContentto associate the popover and the trigger. - The trigger has
aria-expandedset totrueorfalsedepending on the open/closed state of the popover.
Props#
Popover Props#
arrowPadding
arrowPaddingnumber8arrowShadowColor
arrowShadowColorThe `box-shadow` of the popover arrow
stringarrowSize
arrowSizeThe size of the popover arrow
numberautoFocus
autoFocusIf true, focus will be transferred to the first interactive element
when the popover opens
booleantrueboundary
boundaryThe boundary area for the popper. Used within the preventOverflow modifier
"clippingParents" | "scrollParent" | HTMLElementclippingParentscloseDelay
closeDelaynumber200closeOnBlur
closeOnBlurIf true, the popover will close when you blur out it by
clicking outside or tabbing out
booleantruecloseOnEsc
closeOnEscIf true, the popover will close when you hit the Esc key
booleantruecomputePositionOnMount
computePositionOnMountIf true, the popover will be positioned when it mounts
(even if it's not open)
Note 🚨: We don't recommend using this in a popover/menu intensive UI or page
as it might affect scrolling performance.
booleandefaultIsOpen
defaultIsOpenIf true, the popover will be initially opened.
booleandirection
directionTheme direction ltr or rtl. Popper's placement will
be set accordingly
"ltr" | "rtl"ltreventListeners
eventListenersIf provided, determines whether the popper will reposition itself on scroll
and resize of the window.
type ONLY_FOR_FORMAT =
| boolean
| { scroll?: boolean | undefined resize?: boolean | undefined }trueflip
flipIf true, the popper will change its placement and flip when it's
about to overflow its boundary area.
booleantruegutter
gutterThe distance or margin between the reference and popper.
It is used internally to create an offset modifier.
NB: If you define offset prop, it'll override the gutter.
number8id
idThe html id attribute of the popover.
If not provided, we generate a unique id.
This id is also used to auto-generate the `aria-labelledby`
and `aria-describedby` attributes that points to the PopoverHeader and PopoverBody
stringinitialFocusRef
initialFocusRefThe ref of the element that should receive focus when the popover opens.
RefObject<{ focus(): void }>isLazy
isLazyPerformance 🚀:
If true, the PopoverContent rendering will be deferred
until the popover is open.
booleanisOpen
isOpenIf true, the popover will be opened in controlled mode.
booleanlazyBehavior
lazyBehaviorPerformance 🚀: The lazy behavior of popover's content when not visible. Only works when `isLazy={true}` - "unmount": The popover's content is always unmounted when not open. - "keepMounted": The popover's content initially unmounted, but stays mounted when popover is open.
LazyModeunmountmatchWidth
matchWidthIf true, the popper will match the width of the reference at all times.
It's useful for autocomplete, `date-picker` and select patterns.
booleanfalsemodifiers
modifiersArray of popper.js modifiers. Check the docs to see the list of possible modifiers you can pass.
Partial<Modifier<string, any>>[]offset
offsetThe main and cross-axis offset to displace popper element from its reference element.
[number, number]onClose
onCloseCallback fired when the popover closes
() => voidonOpen
onOpenCallback fired when the popover opens
() => voidopenDelay
openDelaynumber200placement
placementThe placement of the popper relative to its reference.
PlacementWithLogicalbottompreventOverflow
preventOverflowIf true, will prevent the popper from being cut off and ensure
it's visible within the boundary area.
booleantruereturnFocusOnClose
returnFocusOnCloseIf true, focus will be returned to the element that triggers the popover
when it closes
booleantruestrategy
strategyThe CSS positioning strategy to use.
"absolute" | "fixed"absolutetrigger
triggerThe interaction that triggers the popover.
hover - means the popover will open when you hover with mouse or
focus with keyboard on the popover trigger
click - means the popover will open on click or
press Enter to Space on keyboard
"click" | "hover"clickOther Props#
PopoverContentcomposesBoxand has the ability to smartly position itself. Thanks to popper.js.PopoverArrow,PopoverHeader,PopoverFooterandPopoverBodycomposesBox.PopoverCloseButtoncomposesBoxcomponent.
The Popover component is a multipart component. The styling needs to be
applied to each part specifically.
To learn more about styling multipart components, visit the Component Style page.
Anatomy#
- A:
content - B:
header - C:
body - D:
footer - E:
popper - F:
arrow - G:
closeButton
Theming properties#
The Popover doesn't have any default properties that affect the theming,
however it is possible to create the following properties:
sizevariantcolorScheme
Theming utilities#
createMultiStyleConfigHelpers: a function that returns a set of utilities for creating style configs for a multipart component (definePartsStyleanddefineMultiStyleConfig).definePartsStyle: a function used to create multipart style objects.defineMultiStyleConfig: a function used to define the style configuration for a multipart component.
import { popoverAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)
Customizing the default theme#
import { popoverAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)const baseStyle = definePartsStyle({// define the part you're going to stylebody: {bg: 'gray.800', // change the background of the body to gray.800},content: {padding: 3, // change the padding of the content},})export const popoverTheme = defineMultiStyleConfig({ baseStyle })
After customizing the popover theme, we can import it in our theme file and add
it in the components property:
import { extendTheme } from '@chakra-ui/react'import { popoverTheme } from './theme/components/popover.ts'export const theme = extendTheme({components: { Popover: popoverTheme },})
This is a crucial step to make sure that any changes that we make to the popover theme are applied.
Adding a custom size#
Let's assume we want to include an extra large popover size. Here's how we can do that:
import { popoverAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)const sizes = {xl: definePartsStyle({header: defineStyle({padding: 14}),content: defineStyle({fontSize: "2xl",marginLeft: 6})}),}export const popoverTheme = defineMultiStyleConfig({ sizes })// Now we can use the new `xl` size<Popover size="xl">...</Popover>
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 { popoverAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)const custom = definePartsStyle({content: defineStyle({padding: 7bg: "gray.700"}),footer: defineStyle({fontSize: "xl"})})export const popoverTheme = defineMultiStyleConfig({variants: { custom },})// Now we can use the new `custom` variant<Popover variant="custom">...</Popover>
Creating the default properties#
Let's assume we want to create the default property for the Popover. Here's
how we can do that:
import { popoverAnatomy as parts } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { defineMultiStyleConfig } =createMultiStyleConfigHelpers(parts.keys)//Let's assume we need each popover to have red contentconst defaultVariant = definePartsStyle({content: {bg: "red.200"}})export const popoverTheme = defineMultiStyleConfig({variant: {default: defaultVariant}defaultProps: {variant: default},})//Now by default, each Popover will have a 'default' variant with red content.
Showcase#
import { Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverBody, PopoverArrow, Button, Flex, Box, IconButton, useColorMode, Text, HStack, Avatar, AvatarBadge, VStack, StackDivider, } from "@chakra-ui/react"; import { FaMoon, FaSun } from "react-icons/fa"; const CustomButton = ({ label, notifications, colorMode }) => ( <Box h="40px" cursor="pointer" p={2} _hover={{ borderBottomWidth: "2px", borderBottomColor: colorMode === "light" ? "blue.600" : "blue.200", }} > <HStack> <Box> <Text fontSize={"sm"}>{label}</Text> </Box> {notifications && ( <Box bg={colorMode === "light" ? "blue.50" : "blue.300"} pl={1.5} pr={1.5} borderRadius="full" > <Text fontSize={"xs"}>{notifications}</Text> </Box> )} </HStack> </Box> ); const Notification = ({ name, src, action, time, hasBadge, colorMode, ...rest }) => ( <Box bg={ hasBadge ? colorMode === "light" ? "gray.100" : "gray.600" : "transparent" } {...rest} > <Flex gap={3} p={2}> <Box> <Avatar size="sm" name={name} src={src}> {hasBadge && ( <AvatarBadge placement="top-start" bg={colorMode === "light" ? "blue.600" : "blue.300"} borderWidth="2px" boxSize="12px" /> )} </Avatar> </Box> <VStack> <Box> <Text fontSize={"sm"}> <b>{name}</b> {action} </Text> </Box> <Box alignSelf={"flex-start"} mt="0 !important"> <Text fontSize={"xs"} fontWeight="lighter"> {time} </Text> </Box> </VStack> </Flex> </Box> ); const PopoverExample = ({ name, colorMode, ...rest }) => ( <Box> <Popover size="md" {...rest}> <PopoverTrigger> <Button variant="outline">{name}</Button> </PopoverTrigger> <PopoverContent> <PopoverArrow /> <PopoverHeader> <Flex justify="space-between" align="center" p={2}> <Box> <Text as="b">Notifications</Text> </Box> <Box> <Button size="xs" colorScheme={"blue"} variant="ghost"> Mark all as read </Button> </Box> </Flex> <Flex gap={4}> <CustomButton label={"All"} notifications={2} colorMode={colorMode} /> <CustomButton label={"Following"} colorMode={colorMode} /> <CustomButton label={"Archive"} colorMode={colorMode} /> </Flex> </PopoverHeader> <PopoverBody> <VStack justify="flex-start" divider={<StackDivider bg="gray.600" m={"0 !important"} />} align="stretch" > <Notification name="Segun Adebayo" action="created Chakra UI" time="10 min ago" src="https://bit.ly/sage-adebayo" hasBadge={true} colorMode={colorMode} /> <Notification name="Dan Abramov" action="created Redux" time="1 hour ago" src="https://bit.ly/dan-abramov" hasBadge={true} colorMode={colorMode} /> <Notification name="Gleb Koshcheev" action="added theme docs" time="12:10pm" colorMode={colorMode} /> <Notification name="Jacob Jones" action="deleted profile image" time="11:30am" colorMode={colorMode} /> </VStack> </PopoverBody> </PopoverContent> </Popover> </Box> ); export default function App() { const { toggleColorMode, colorMode } = useColorMode(); return ( <Box pos="relative"> <Flex direction="column" gap={3} align="center" p={8} alignItems={"center"} justify="center" > <PopoverExample name={"Themed Popover"} colorMode={colorMode} /> <PopoverExample name={"Themed XL Popover"} size="xl" colorMode={colorMode} /> <PopoverExample name={"Themed XL Rounded Popover"} size="xl" variant="rounded" colorMode={colorMode} /> </Flex> <Box> <IconButton aria-label="toggle theme" rounded="full" size="xs" position="fixed" bottom={4} left={4} onClick={toggleColorMode} icon={colorMode === "dark" ? <FaSun /> : <FaMoon />} /> </Box> </Box> ); }