Popover
Display rich content within a panel in context of a trigger button
Basic example
Popovers are a simple way to group contextual actions into a toggle-able overlay that stays attached to the trigger button. Use Popovers to enclose sets of related actions. Avoid using Popovers to contain miscellaneous or unrelated actions.
Editable example<Popover
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Show popover
</Button>
)}
>
<h2 className="text-h4">Popover panel content</h2>
<p className="text-paragraph">You can put whatever you want in here</p>
<TextInput label="Example input" />
</Popover>
Button
Any clickable element can be used as a button to trigger the Popover. Make sure whatever component you use renders a <button> HTML element. Most of the time a or should be used, but for special cased you can use a custom button element.
The button prop uses render props to pass through the onClick event and other properties for accessibility. The easiest way to use the render props is to spread them onto the button component.
Editable example<Popover
button={(buttonProps) => (
<IconButton
icon={<Edit />}
tooltip={{ content: "Edit" }}
{...buttonProps}
/>
)}
>
<span>Popover content</span>
</Popover>
Size
Choose from one of the preset sizes. For special cases you can use PopoverSize.Auto and the the panel will match the size of its content.
Editable example<Popover
size={PopoverSize.Small}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Small
</Button>
)}
>
<span>Small Popover</span>
</Popover>
<Popover
size={PopoverSize.Medium}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Medium
</Button>
)}
>
<span>Medium Popover</span>
</Popover>
<Popover
size={PopoverSize.Large}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Large
</Button>
)}
>
<span>Large Popover</span>
</Popover>
<Popover
size={PopoverSize.Auto}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Auto
</Button>
)}
>
<span>Auto size</span>
</Popover>
Position
Set a preferred position for the Popover to appear. Appears to the bottom by default. If there's no space in the viewport for the preferred position, it will automatically flip to the other side.
Editable example<Popover
position={PopoverPosition.Top}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>Top</Button>
)}
>
<span>Popover content</span>
</Popover>
<Popover
position={PopoverPosition.Bottom}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>Bottom</Button>
)}
>
<span>Popover content</span>
</Popover>
<Popover
position={PopoverPosition.Left}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>Left</Button>
)}
>
<span>Popover content</span>
</Popover>
<Popover
position={PopoverPosition.Right}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>Right</Button>
)}
>
<span>Popover content</span>
</Popover>
Alignment
Control the alignment of the Popover panel along the axis set by position. Options are PopoverAlignment.Start and PopoverAlignment.Center (the default).
Editable example<Popover
alignment={PopoverAlignment.Start}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Start-aligned
</Button>
)}
>
<span>Aligned to the start of the trigger</span>
</Popover>
<Popover
alignment={PopoverAlignment.Center}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Center-aligned (default)
</Button>
)}
>
<span>Centered on the trigger</span>
</Popover>
Padding
Control the inner padding of the Popover panel. Accepts "small", "medium" (default), or "none".
Editable example<Popover
padding="none"
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
No padding
</Button>
)}
>
<span>Edge-to-edge content</span>
</Popover>
<Popover
padding="small"
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Small padding
</Button>
)}
>
<span>Small padding content</span>
</Popover>
Offset
Set the distance in pixels between the Popover panel and its trigger button. Defaults to 4.
Editable example<Popover
offset={16}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Large offset
</Button>
)}
>
<span>16px away from the button</span>
</Popover>
Max height
Use maxHeight to constrain the panel height. Accepts a number (pixels) or CSS string value.
Editable example<Popover
maxHeight={150}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Constrained height
</Button>
)}
>
<div style={{ height: 300 }}>Tall content that will scroll</div>
</Popover>
Caret
Displays an arrow pointing to the center of the trigger button from the Popover panel.
Editable example<Popover
caret
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Popover with caret
</Button>
)}
>
<span>Popover content</span>
</Popover>
Dark theme
Specify a dark theme for the Popover panel. The default theme is light.
Editable example<Popover
theme={PopoverTheme.Dark}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>Dark popover</Button>
)}
>
<span>Popover content</span>
</Popover>
Controlled
By default, Popover automatically handles its open and closed state. In some cases you need to be able to directly control the state of the Popover externally. In these situations you can make it a controlled component with the isOpen and onChange props.
One thing to keep in mind is if you want the external trigger to toggle the Popover it will clash with the Popover's outer mouseDown handler, so you'll need to add event.nativeEvent.stopPropagation() on your external trigger's onMouseDown.
Editable example() => {
const [popoverOpen, setPopoverOpen] = React.useState(false);
return (
<>
<Button
type="secondary"
decorator={ButtonDecorator.Arrow}
onMouseDown={(event) => {
if (popoverOpen) {
event.nativeEvent.stopPropagation();
}
}}
onClick={() => {
setPopoverOpen(!popoverOpen);
}}
>
Trigger Popover
</Button>
<Popover
isOpen={popoverOpen}
onChange={setPopoverOpen}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>Popover target</Button>
)}
>
<span>Popover content</span>
</Popover>
</>
);
}
Close details
When using a controlled Popover, the onChange callback receives a second argument — a PopoverChangeDetails object — that tells you why the Popover is closing. This is useful when you want to ignore certain close reasons, for example keeping the Popover open when its trigger scrolls off-screen.
The reason field can be one of:
| Reason | Description |
|---|---|
clicked-button | The user clicked the trigger button to close |
clicked-outside | The user clicked outside the Popover panel |
scrolled-away | The trigger button scrolled out of the viewport |
pressed-escape | The user pressed the Escape key |
Editable example() => {
const [popoverOpen, setPopoverOpen] = React.useState(false);
return (
<Popover
isOpen={popoverOpen}
onChange={(isOpen, details) => {
// Ignore close requests triggered by the button scrolling off-screen
if (!isOpen && details.reason === "scrolled-away") {
return;
}
setPopoverOpen(isOpen);
}}
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Stays open on scroll-away
</Button>
)}
>
<span>This Popover won't close when it scrolls out of view</span>
</Popover>
);
}
Styling
To override the styling of the Popover panel, add a className and drop your styles in there. This is useful if you need to remove the default padding for edge-to-edge content, or if you need to add a specific z-index.
Editable example<style>
{`
.custom-popover-example {
z-index: 128;
}
.custom-popover-example .popover__panel-content {
padding: 0;
}
`}
</style>
<Popover
className="custom-popover-example"
button={(buttonProps) => (
<Button type="secondary" {...buttonProps}>
Popover with custom styles
</Button>
)}
>
<span>No padding!</span>
</Popover>
Accessibility
- Popover automatically handles focus management when interacting via a keyboard
- Be careful with nesting Popovers — currently it has issues with the escape key closing the whole stack
- When using a custom element with the button prop make sure it's actually rendering an HTML
<button>element so that it can be interacted with via a keyboard.