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
PopoverTrigger
is 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
PopoverContent
is 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
click
trigger, nothover
.
function WalkthroughPopover() {
const initialFocusRef = React.useRef();
return (
<Popover
initialFocusRef={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 do
eiusmod tempor incididunt ut labore et dolore.
</PopoverBody>
<PopoverFooter
border="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 component
const 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 form
const Form = ({ firstFieldRef, onCancel }) => {
return (
<Stack spacing={4}>
<TextInput
label="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 click
const PopoverForm = () => {
const { onOpen, onClose, isOpen } = useDisclosure();
const firstFieldRef = React.useRef(null);
return (
<>
<Box display="inline-block" mr={3}>
John Smith
</Box>
<Popover
isOpen={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>
<Popover
returnFocusOnClose={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
PopoverAnchor
ensure 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 (
<Popover
isOpen={isEditing}
onOpen={setIsEditing.on}
onClose={setIsEditing.off}
closeOnBlur={false}
isLazy
lazyBehavior="keepMounted"
>
<HStack>
<PopoverAnchor>
<Input
color={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>
<Button
mt={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>
<Box
tabIndex="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 the
props 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 eiusmod
tempor 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 eiusmod
tempor incididunt ut labore et dolore.
</PopoverBody>
</PopoverContent>
</Popover>
Accessibility
When you see the word "trigger", it is referring to the
children
ofPopoverTrigger
Keyboard and Focus
- When the popover is opened, focus is moved to the
PopoverContent
. If theinitialFocusRef
is set, then focus moves to the element with thatref
. - When the popover is closed, focus returns to the trigger. If you set
returnFocusOnClose
tofalse
, 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
Space
orEnter
when 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
Esc
key while the popover is open and focus is within thePopoverContent
, will close the popover. If you setcloseOnEsc
tofalse
, it will not close. - Clicking outside or blurring out of the
PopoverContent
closes the popover. If you setcloseOnBlur
tofalse
, it will not close.
ARIA Attributes
- If the trigger is set to
click
, thePopoverContent
element has role set todialog
. If the trigger is set tohover
, thePopoverContent
hasrole
set totooltip
. - The
PopoverContent
hasaria-labelledby
set to theid
of thePopoverHeader
. - The
PopoverContent
hasaria-describedby
set to theid
of thePopoverBody
. - The
PopoverContent
hasaria-hidden
set totrue
orfalse
depending on the open/closed state of the popover. - The trigger has
aria-haspopup
set totrue
to denote that it triggers a popover. - The trigger has
aria-controls
set to theid
of thePopoverContent
to associate the popover and the trigger. - The trigger has
aria-expanded
set totrue
orfalse
depending on the open/closed state of the popover.
Props
Popover Props
arrowPadding
arrowPadding
The padding required to prevent the arrow from reaching the very edge of the popper.
number
8
arrowShadowColor
arrowShadowColor
The `box-shadow` of the popover arrow
string
arrowSize
arrowSize
The size of the popover arrow
number
autoFocus
autoFocus
If true
, focus will be transferred to the first interactive element
when the popover opens
boolean
true
boundary
boundary
The boundary area for the popper. Used within the preventOverflow
modifier
HTMLElement | "clippingParents" | "scrollParent"
"clippingParents"
children
children
The content of the popover. It is usually the PopoverTrigger
,
and PopoverContent
MaybeRenderProp<{ isOpen: boolean; onClose: () => void; forceUpdate: (() => void); }>
closeDelay
closeDelay
number
200
closeOnBlur
closeOnBlur
If true
, the popover will close when you blur out it by
clicking outside or tabbing out
boolean
true
closeOnEsc
closeOnEsc
If true
, the popover will close when you hit the Esc
key
boolean
true
colorScheme
colorScheme
Color Schemes for Popover
are not implemented in the default theme. You can extend the theme to implement them.
string
computePositionOnMount
computePositionOnMount
If 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.
boolean
defaultIsOpen
defaultIsOpen
If true
, the popover will be initially opened.
boolean
direction
direction
Theme direction ltr
or rtl
. Popper's placement will
be set accordingly
"ltr" | "rtl"
"ltr"
eventListeners
eventListeners
If provided, determines whether the popper will reposition itself on scroll
and resize
of the window.
boolean | { scroll?: boolean; resize?: boolean; } | undefined
true
flip
flip
If true
, the popper will change its placement and flip when it's
about to overflow its boundary area.
boolean
true
gutter
gutter
The 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.
number
8
id
id
The 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
string
initialFocusRef
initialFocusRef
The ref
of the element that should receive focus when the popover opens.
RefObject<{ focus(): void; }>
isLazy
isLazy
Performance 🚀:
If true
, the PopoverContent rendering will be deferred
until the popover is open.
boolean
isOpen
isOpen
If true
, the popover will be opened in controlled mode.
boolean
lazyBehavior
lazyBehavior
Performance 🚀: 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.
LazyMode
"unmount"
matchWidth
matchWidth
If true
, the popper will match the width of the reference at all times.
It's useful for autocomplete
, `date-picker` and select
patterns.
boolean
modifiers
modifiers
Array of popper.js modifiers. Check the docs to see the list of possible modifiers you can pass. @see Docs https://popper.js.org/docs/v2/modifiers/
Partial<Modifier<string, any>>[]
offset
offset
The main and cross-axis offset to displace popper element from its reference element.
[number, number]
onClose
onClose
Callback fired when the popover closes
(() => void)
onOpen
onOpen
Callback fired when the popover opens
(() => void)
openDelay
openDelay
number
200
orientation
orientation
"horizontal" | "vertical"
placement
placement
The placement of the popper relative to its reference.
PlacementWithLogical
"bottom"
preventOverflow
preventOverflow
If true
, will prevent the popper from being cut off and ensure
it's visible within the boundary area.
boolean
true
returnFocusOnClose
returnFocusOnClose
If true
, focus will be returned to the element that triggers the popover
when it closes
boolean
true
size
size
Sizes for Popover
are not implemented in the default theme. You can extend the theme to implement them.
string
strategy
strategy
The CSS positioning strategy to use.
"fixed" | "absolute"
"absolute"
styleConfig
styleConfig
Record<string, any>
trigger
trigger
The 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"
"click"
variant
variant
Variants for Popover
are not implemented in the default theme. You can extend the theme to implement them.
string
Other Props
PopoverContent
composesBox
and has the ability to smartly position itself. Thanks to popper.js.PopoverArrow
,PopoverHeader
,PopoverFooter
andPopoverBody
composesBox
.PopoverCloseButton
composesBox
component.