Popover
A floating panel anchored to a trigger element that displays a selectable list of items. Supports keyboard navigation, grouped items, custom rendering, built-in filtering, and mobile-responsive layout.
Basic Usage
Simple popover with selectable items
Click the button to open a popover list and select an item.
const [isOpen, setIsOpen] = useState(false);
const [selected, setSelected] = useState<ListItem | null>(null);
const triggerRef = useRef<HTMLButtonElement>(null);
<Button ref={triggerRef} onClick={() => setIsOpen(!isOpen)}>
{selected ? selected.label : 'Pick a fruit'}
</Button>
<Popover
isOpen={isOpen}
onClose={() => setIsOpen(false)}
triggerElement={triggerRef.current}
items={fruitItems}
onSelect={(item) => {
setSelected(item);
setIsOpen(false);
}}
selectedIndex={fruitItems.findIndex(
(i) => i.value === selected?.value
)}
/>Grouped Items
Grouped list items
Items organized into labeled groups with section headers.
const groups = [
{
label: 'Fruits',
items: [
{ label: 'Apple', value: 'apple' },
{ label: 'Banana', value: 'banana' },
],
},
{
label: 'Vegetables',
items: [
{ label: 'Carrot', value: 'carrot' },
{ label: 'Broccoli', value: 'broccoli' },
],
},
];
<Popover
isOpen={isOpen}
onClose={() => setIsOpen(false)}
triggerElement={triggerRef.current}
items={allItems}
groups={groups}
onSelect={(item) => {
setSelected(item);
setIsOpen(false);
}}
/>Controlled Open/Close
Controlled open and close
Programmatically control the popover with separate open and close buttons.
const [isOpen, setIsOpen] = useState(false);
const triggerRef = useRef<HTMLButtonElement>(null);
<div className="flex gap-3">
<Button ref={triggerRef} onClick={() => setIsOpen(true)}>
Open Popover
</Button>
<Button variant="danger" layout="outlined"
onClick={() => setIsOpen(false)}>
Close Popover
</Button>
</div>
<Popover
isOpen={isOpen}
onClose={() => setIsOpen(false)}
triggerElement={triggerRef.current}
items={colorItems}
onSelect={(item) => {
setSelected(item);
setIsOpen(false);
}}
/>Custom Render Item
Custom renderItem
Use the renderItem prop to fully customize how each list item is rendered.
const renderItem = (item, index, isSelected, isHighlighted) => (
<div
className={cn('eui-popover-item', {
'eui-popover-item-highlighted': isHighlighted,
'eui-popover-item-selected': isSelected,
})}
onClick={() => handleSelect(item, index)}
onMouseEnter={() => setHighlighted(index)}
>
<span
style={{
width: 8, height: 8,
borderRadius: '50%',
backgroundColor: statusColors[item.value],
}}
/>
<span>{item.label}</span>
</div>
);
<Popover
isOpen={isOpen}
onClose={() => setIsOpen(false)}
triggerElement={triggerRef.current}
items={statusItems}
onSelect={handleSelect}
renderItem={renderItem}
width="180px"
/>Filterable Popover
Filterable popover with children
Use the filter prop alongside children to render a search input above the list.
const [filter, setFilter] = useState('');
<Popover
isOpen={isOpen}
onClose={() => setIsOpen(false)}
triggerElement={triggerRef.current}
items={fruitItems}
filter={filter}
onSelect={(item) => {
setSelected(item);
setIsOpen(false);
}}
emptyMessage="No fruits match your search"
>
<div style={{ padding: '8px' }}>
<TextInput
value={filter}
onChange={setFilter}
placeholder="Search fruits..."
/>
</div>
</Popover>Import
import { Popover } from 'fluxo-ui';API Reference
isOpenreqbooleanControls whether the popover is visible.
isOpenreqbooleanControls whether the popover is visible.
onClosereq(e?: MouseEvent) => voidCallback invoked when the popover should close (click outside, Escape key).
onClosereq(e?: MouseEvent) => voidCallback invoked when the popover should close (click outside, Escape key).
triggerElementreqHTMLElement | nullThe DOM element the popover is anchored to for positioning.
triggerElementreqHTMLElement | nullThe DOM element the popover is anchored to for positioning.
itemsreqListItem[]Array of list items to display inside the popover.
itemsreqListItem[]Array of list items to display inside the popover.
groupsListItemGroup[]Optional grouping of items with labeled section headers.
groupsListItemGroup[]Optional grouping of items with labeled section headers.
onSelectreq(item: ListItem, index: number) => voidCallback invoked when an item is selected.
onSelectreq(item: ListItem, index: number) => voidCallback invoked when an item is selected.
selectedIndexnumber"-1"Index of the currently selected item (shows a check mark).
selectedIndexnumber"-1"Index of the currently selected item (shows a check mark).
renderItem(item, index, isSelected, isHighlighted) => ReactNodeCustom render function for each list item.
renderItem(item, index, isSelected, isHighlighted) => ReactNodeCustom render function for each list item.
maxHeightstring"'300px'"Maximum height of the popover container before scrolling.
maxHeightstring"'300px'"Maximum height of the popover container before scrolling.
widthstringWidth of the popover. Defaults to the trigger element's width.
widthstringWidth of the popover. Defaults to the trigger element's width.
filterstring"''"Filter string to narrow down displayed items by label.
filterstring"''"Filter string to narrow down displayed items by label.
loadingboolean"false"Shows a loading spinner instead of items.
loadingboolean"false"Shows a loading spinner instead of items.
emptyMessagestring"'No items found'"Message displayed when no items match the filter.
emptyMessagestring"'No items found'"Message displayed when no items match the filter.
childrenReactNodeContent rendered above the item list (e.g., a search input).
childrenReactNodeContent rendered above the item list (e.g., a search input).
Features
Portal Rendering
Renders in a portal so the popover is never clipped by overflow:hidden containers or stacking contexts.
Keyboard Navigation
Full keyboard support with ArrowUp, ArrowDown to navigate, Enter to select, and Escape to close.
Smart Positioning
Automatically calculates the best position relative to the trigger element using usePosition hook.
Grouped Items
Organize items into labeled groups with section headers for better categorization.
Custom Rendering
Supply a renderItem function to fully control the appearance of each item in the list.
Built-in Filtering
Pass a filter string to narrow items by label, and use the children slot to render a search input.
Mobile Responsive
Adapts to mobile viewports with a full-width bottom sheet layout using the useMobile hook.
Click Outside to Close
Clicking anywhere outside the popover automatically closes it via the useClickOutside hook.