Popover
Floating content panel anchored to a trigger element with configurable placement, auto-flip, and dismiss behavior.
Overview
The Popover component renders a floating content panel anchored to a
trigger element. It supports 6 placement positions with automatic flip
when near viewport edges, and dismisses on outside click or Escape key.
Popover is the foundational building block for AutocompleteSelect, MultiSelectDropdown, and ActionMenu.
import { Popover, Button } from '@enara-health/ui-react';
const [isOpen, setIsOpen] = useState(false);
<Popover
open={isOpen}
onOpenChange={setIsOpen}
trigger={<Button>Open Popover</Button>}
>
<p>Popover content goes here.</p>
</Popover> Note: Popover is an interactive component that requires state
management. To see it in action, run nx dev @enara-health/ui-react
and view the component in the dev preview.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Required. Whether the popover is visible |
onOpenChange | (open: boolean) => void | - | Required. Callback when open state changes |
trigger | ReactNode | - | Required. Element that anchors and toggles the popover |
children | ReactNode | - | Required. Content rendered inside the floating panel |
placement | 'bottom-start' | 'bottom-end' | 'bottom' | 'top-start' |
'top-end' | 'top' | 'bottom-start' | Preferred position relative to the trigger |
contentPadding | 'none' | 'default' | 'default' | Padding inside the floating panel |
matchTriggerWidth | boolean | false | Whether the popover should match the trigger's width |
className | string | - | Additional CSS class names for the popover panel |
Variants
Placement
Popover supports 6 placement positions. When there isn't enough space in the preferred direction, it automatically flips to the opposite side.
bottom-start default bottom bottom-end top-start top top-end <Popover placement="bottom-start" ...> {/* Left-aligned below trigger */}
<Popover placement="bottom" ...> {/* Centered below trigger */}
<Popover placement="bottom-end" ...> {/* Right-aligned below trigger */}
<Popover placement="top-start" ...> {/* Left-aligned above trigger */}
<Popover placement="top" ...> {/* Centered above trigger */}
<Popover placement="top-end" ...> {/* Right-aligned above trigger */} Content Padding
Use contentPadding="none" when the popover contains custom layouts
like menu lists or autocomplete results that manage their own spacing.
// Default padding (for general content)
<Popover open={isOpen} onOpenChange={setIsOpen} trigger={<Button>Info</Button>}>
<p>Some helpful information.</p>
</Popover>
// No padding (for custom layouts like menus)
<Popover open={isOpen} onOpenChange={setIsOpen} trigger={<Button>Menu</Button>} contentPadding="none">
<MenuList items={menuItems} />
</Popover> Match Trigger Width
When matchTriggerWidth is true, the popover panel matches the width
of its trigger element. Useful for select-like dropdowns.
<Popover
open={isOpen}
onOpenChange={setIsOpen}
trigger={<Button style={{ width: 200 }}>Select Option</Button>}
matchTriggerWidth
>
<OptionsList />
</Popover> Examples
Dropdown Filter Panel
Use a popover to show filter options below a trigger button.
const [isOpen, setIsOpen] = useState(false);
<Popover open={isOpen} onOpenChange={setIsOpen} trigger={<Button>Filter</Button>}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<Checkbox label="Active" checked={filters.active} onChange={handleToggle('active')} />
<Checkbox label="Pending" checked={filters.pending} onChange={handleToggle('pending')} />
<Checkbox label="Archived" checked={filters.archived} onChange={handleToggle('archived')} />
</div>
</Popover> Contextual Info
Display additional information on demand without navigating away.
<Popover
open={isOpen}
onOpenChange={setIsOpen}
trigger={<IconButton icon={<Info />} aria-label="More info" />}
placement="bottom-end"
>
<Text size="sm" color="muted">
This metric is calculated from the last 30 days of activity.
</Text>
</Popover> Accessibility
- Keyboard Support: Escape key closes the popover. Trigger inherits keyboard activation from its element type.
- Focus Management: Focus remains with the trigger element. Consumers should manage focus within popover content as needed.
- Screen Reader: Content is only in the DOM when open, preventing hidden content from being announced.
- ARIA: Consumers should add appropriate ARIA attributes (e.g.,
aria-haspopup,aria-expanded) to the trigger based on the use case. - WCAG Compliance: Meets AA standards when properly configured with ARIA attributes.
Best Practices
-
Add
aria-haspopupandaria-expandedto trigger elements for screen readers -
Don't use Popover for tooltip-like content — use
Tooltipinstead -
Don't use Popover for modal interactions — use
Dialoginstead - The trigger must be a single React element (not a string or fragment)
- Keep popover content focused and concise