Components

Popover

Positioned disclosure content with optional anchor, modal behavior, focus guards, arrow geometry, and close controls.

When to Use

Use Popover for a small interactive panel that belongs to a trigger, such as quick actions or compact settings. Use Tooltip for short, non-interactive help, HoverCard for preview content, and Dialog when the user must focus on a larger task. A popover should add helpful choices without becoming a whole page inside a floating box.

Features

  • Controlled and uncontrolled open state.
  • Click or hover trigger mode.
  • Optional anchor separate from the trigger.
  • Floating UI positioning with side, align, side offset, collision shift, flip, and arrow coordinates.
  • Modal mode with focus trap and scroll lock.
  • Non-modal focus guards and outside interaction dismissal.
  • Stack-aware Escape dismissal for nested overlays.
  • Close button part and portal support.

Import

import { Popover } from "@flowstack-ui/atom";

Anatomy

<Popover.Root>
  <Popover.Anchor />
  <Popover.Trigger />
  <Popover.Portal>
    <Popover.Content>
      <Popover.Arrow />
      <Popover.Close />
    </Popover.Content>
  </Popover.Portal>
</Popover.Root>

API Reference

Root

Owns open state, dismissal rules, trigger mode, and the references used to position Content. Root renders no DOM element.

PropTypeDefault
openboolean-
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void-
modalbooleanfalse
triggerMode"click" | "hover""click"
openDelaynumber200
closeDelaynumber300
closeOnEscapebooleantrue
closeOnInteractOutsidebooleantrue
disabledbooleanfalse

ARIA: Root renders no element and adds no ARIA attributes.

Data attributes: Root renders no element and exposes no data attributes.

Anchor

Optional positioning reference. Use it when content should be positioned relative to a different element than the trigger.

PropTypeDefault
asChildbooleanfalse
renderRenderProp-

ARIA: Anchor adds no roles or ARIA attributes.

Data attributeValues
[data-slot]"popover-anchor"

Trigger

Opens or toggles the popover and connects the button to Content. In hover mode, it also starts the configured open and close delays.

PropTypeDefault
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
role"button" for a custom rendered element
aria-haspopup"dialog"
aria-expandedCurrent open state
aria-controlsContent ID while open
aria-disabledtrue when Root is disabled
Data attributeValues
[data-slot]"popover-trigger"
[data-state]"open" | "closed"
[data-trigger-mode]"click" | "hover"
[data-disabled]Present when disabled

Portal

Moves Content to another DOM container so it can escape clipping and stacking ancestors. It renders no wrapper.

PropTypeDefault
containerHTMLElement | nulldocument.body after mount
disabledbooleanfalse

ARIA: Portal adds no roles or ARIA attributes.

Data attributes: Portal renders no wrapper and exposes none.

Content

Renders the positioned dialog, manages outside dismissal, and manages focus. Modal Content traps focus and locks scrolling; non-modal Content closes when focus leaves its trigger/content scope.

PropTypeDefault
side"top" | "right" | "bottom" | "left""bottom"
align"start" | "center" | "end""center"
sideOffsetnumber8
ariaLabelstring-
ARIA attributeValues
role"dialog"
aria-labelValue from ariaLabel when provided
aria-modaltrue in modal mode
Data attributeValues
[data-slot]"popover-content"
[data-state]"open" | "closed"
[data-side]"top" | "right" | "bottom" | "left"
[data-positioned]Present after the first positioning frame

Arrow

Renders a decorative SVG pointer using the actual side chosen after collision handling. getPopoverArrowGeometry exposes the same geometry for custom arrows.

PropTypeDefault
widthnumber10
heightnumber5
asChildbooleanfalse
renderRenderProp-

ARIA: Arrow is decorative and hidden from assistive technology.

Data attributeValues
[data-slot]"popover-arrow"
[data-side]"top" | "right" | "bottom" | "left"

Close

Renders a button that requests Root to close after any consumer click handler runs without preventing the event.

PropTypeDefault
asChildbooleanfalse
renderRenderProp-

ARIA: Close uses native button semantics. Give icon-only controls an aria-label.

Data attributeValues
[data-slot]"popover-close"

Advanced compound parts can read usePopoverContext or usePopoverContentContext. Their matching context providers are also public for low-level composition. getPopoverArrowGeometry returns the SVG geometry used by Arrow for a given side, width, and height.

Examples

Basic Popover

import { Popover } from "@flowstack-ui/atom";

export default function ActionsPopover() {
  return (
    <Popover.Root>
      <Popover.Trigger>Actions</Popover.Trigger>
      <Popover.Portal>
        <Popover.Content ariaLabel="Project actions">
          <button type="button">Duplicate</button>
          <Popover.Close>Done</Popover.Close>
          <Popover.Arrow />
        </Popover.Content>
      </Popover.Portal>
    </Popover.Root>
  );
}

Custom Anchor

import { Popover } from "@flowstack-ui/atom";

export default function AnchoredPopover() {
  return (
    <Popover.Root>
      <Popover.Anchor asChild>
        <span>Account</span>
      </Popover.Anchor>
      <Popover.Trigger>Open account actions</Popover.Trigger>
      <Popover.Portal>
        <Popover.Content side="right" align="start" ariaLabel="Account actions">
          <button type="button">View profile</button>
          <Popover.Close>Close</Popover.Close>
        </Popover.Content>
      </Popover.Portal>
    </Popover.Root>
  );
}

Accessibility

Popover uses the WAI-ARIA dialog pattern for Content and an aria-haspopup="dialog" trigger. Provide ariaLabel when Content does not have another accessible name. In modal mode, focus remains contained inside the popover scope, including registered portalled layers opened by descendants.

KeyDescription
EnterToggles a non-native trigger.
SpaceToggles a non-native trigger.
EscapeCloses the topmost popover when closeOnEscape is enabled.
TabIn modal mode, focus remains trapped inside content. In non-modal mode, focus guards close the popover when tabbing away.

Changelog

See CHANGELOG.md.