Popover
Currently only available in React.
Floating panel anchored to a trigger. Closes on outside click or Escape by default. Controlled and uncontrolled. The component is unstyled and unpositioned — use Tailwind / CSS to anchor the Content relative to the Root.
Features
Closes on outside click and Escape (toggleable).
Can be controlled or uncontrolled.
forceMount keeps Content in the DOM for animation.Close subcomponent for inline close buttons.Full ARIA wiring: aria-haspopup / aria-expanded / aria-controls.
Example
Anatomy
import { Popover } from '@wire-ui/react'
<Popover.Root>
<Popover.Trigger>Open</Popover.Trigger>
<Popover.Content side="bottom" align="center">
<p>Popover content</p>
<Popover.Close>Close</Popover.Close>
</Popover.Content>
</Popover.Root>Styling
Root, Trigger, and Content all expose data-state ("open" / "closed"). Content additionally exposes data-side and data-align. Position the Content manually — wire-ui does not auto-anchor.
<Popover.Content className="
absolute top-full mt-2
data-[state=closed]:hidden
data-[state=open]:animate-in
data-[side=top]:bottom-full data-[side=top]:top-auto data-[side=top]:mb-2 data-[side=top]:mt-0
" />Using data attributes
[data-state="closed"] { display: none; }
[data-state="open"] { animation: fadeIn 120ms ease-out; }
[data-side="top"] { transform-origin: bottom; }
[data-side="bottom"] { transform-origin: top; }
[data-align="start"] { left: 0; }
[data-align="center"] { left: 50%; transform: translateX(-50%); }
[data-align="end"] { right: 0; }Root props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Initial open state (uncontrolled) |
onOpenChange | (open: boolean) => void | — | Called when the open state changes |
closeOnOutsideClick | boolean | true | Close on outside click |
closeOnEscape | boolean | true | Close on Escape key |
Trigger props
Accepts all standard <button> attributes. Clicking toggles the open state. Receives aria-haspopup="dialog", aria-expanded, and aria-controls automatically.
Content props
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Side data attribute for position-aware styling |
align | 'start' | 'center' | 'end' | 'center' | Alignment along the side |
forceMount | boolean | false | Keep mounted in DOM when closed (use with data-state for animation) |
Close props
Accepts all standard <button> attributes. Closes the popover on click.
Data attributes
| Attribute | Element | Values |
|---|---|---|
data-state | Root, Trigger, Content | "open" / "closed" |
data-side | Content | "top" / "right" / "bottom" / "left" |
data-align | Content | "start" / "center" / "end" |
data-hover | Trigger, Close | Mouse is over the button |
data-focus-visible | Trigger, Close | Button has keyboard focus |
data-active | Trigger, Close | Button is being pressed |
Accessibility
- Trigger renders as
<button>witharia-haspopup="dialog",aria-expanded, andaria-controls. - Content is
<div role="dialog">witharia-labelledbypointing at the Trigger. - Focus is left up to the caller — wire-ui does not auto-focus Content (use a focused element inside it if needed).
- Set
closeOnOutsideClick={false}for click-resistant overlays; setcloseOnEscape={false}to require an explicit Close.
Keyboard Interactions
| Key | Description |
|---|---|
| Space / Enter | Toggles the popover when Trigger is focused. |
| Escape | Closes the popover (when closeOnEscape is true). |
| Tab | Moves focus through Trigger, then into Content when open. |
Last updated on