Popper
A floating position primitive powered by @floating-ui/react-dom. The foundation for Dropdown and any anchored overlay.
Overview
Popper positions a floating element relative to an anchor. It wraps @floating-ui/react-dom and manages the CSS visibility toggle (opacity 0/1), click-away detection via ClickAwayEvent, and optional persistent mounting via keepMounted.
Use Popper directly when you need a custom floating overlay. For action lists, use Dropdown or ActionsDropdown which build on top of Popper.
Import
import { Popper } from '@unflow.io/ui/components/Popper';Props
| Prop | Type | Default | Description |
|---|---|---|---|
anchorEl | Element | VirtualElement | required | The element the Popper positions relative to |
open | boolean | false | Whether the Popper is visible |
placement | Placement | 'bottom-start' | Position relative to anchor (12 options from @floating-ui) |
offset | number | 4 | Pixel gap between anchor and Popper |
keepMounted | boolean | false | Keep the Popper in the DOM when closed |
onClickAway | () => void | — | Called when clicking outside the Popper |
clickAwayIgnore | Element | — | Element to exclude from click-away detection |
focusTrap | boolean | false | Traps focus within the Popper while open |
autoFocus | boolean | false | Focuses the Popper (or its first focusable child) on open |
All standard HTMLDivElement props are accepted.
Placement options
top-start top top-end
left-start right-start
left right
left-end right-end
bottom-start bottom bottom-end<Popper anchorEl={anchor} placement="bottom-start" open={isOpen}>
{/* content */}
</Popper>VirtualElement
anchorEl accepts a VirtualElement — an object with a getBoundingClientRect() method. This lets you position a Popper at an arbitrary point (e.g. the cursor position for a context menu):
const virtualAnchor: VirtualElement = {
getBoundingClientRect: () => ({
x: mouseX,
y: mouseY,
width: 0,
height: 0,
top: mouseY,
right: mouseX,
bottom: mouseY,
left: mouseX,
}),
};
<Popper anchorEl={virtualAnchor} open={isOpen}>
<ContextMenu />
</Popper>Basic
<Popper
anchorEl={anchorRef.current}
open={open}
placement="bottom-start"
offset={8}
onClickAway={() => setOpen(false)}
clickAwayIgnore={anchorRef.current ?? undefined}
>
<div className="rounded-xl border border-grey-200 bg-white p-4 shadow-md">
<p className="text-sm font-medium">Popper content</p>
</div>
</Popper>Persistent Popper (keepMounted)
Use keepMounted={true} when the Popper contains stateful content (search input, form) that should survive close/open cycles:
<Popper anchorEl={anchor} open={isOpen} keepMounted>
<SearchDropdown />
</Popper>When closed, the Popper is still in the DOM but invisible (opacity-0). This means the search input retains its value between opens.
Implementation notes
Popper uses CSS opacity for visibility rather than conditional rendering. This means the Popper's DOM node is always present after first mount (even when keepMounted={false}), which avoids a brief layout flash on first open. The keepMounted prop controls whether the node is mounted at all before the first open={true}.
Position updates are computed by @floating-ui/react-dom's computePosition after every render that changes open, anchorEl, or placement.
Accessibility
Popper has no built-in ARIA attributes. Depending on the content, you may need:
<Popper anchorEl={anchor} open={isOpen} role="dialog" aria-modal="true">
{/* modal content */}
</Popper>For dropdown menus, Dropdown handles role="menu" and role="menuitem" via ActionsDropdown.
SnackbarProvider
Owns a queue of snackbars and renders them stacked at any screen position — wrap your app once, then call useSnackbar() anywhere to show one.
Tooltip
A hover-triggered floating label powered by Popper. Accepts plain text or rich React content and supports all 12 Popper placement options.