Skip to content
UI

Popover

A small panel anchored to a button that holds a few controls or a short explanation.

@zenoxai/ui/components/popoverBase UI Markdown
<Popover>
  <PopoverTrigger render={<Button variant="outline" />}>
    Snooze
  </PopoverTrigger>
  <PopoverContent>
    <PopoverHeader>
      <PopoverTitle>Snooze alert</PopoverTitle>
      <PopoverDescription>
        Hide it from the queue. It comes back if the rule fires again.
      </PopoverDescription>
    </PopoverHeader>
    <div className="grid grid-cols-3 gap-2">
      <Button variant="outline" size="sm">
        1 hour
      </Button>
      <Button variant="outline" size="sm">
        4 hours
      </Button>
      <Button variant="outline" size="sm">
        1 day
      </Button>
    </div>
  </PopoverContent>
</Popover>

Usage

import { Button } from "@zenoxai/ui/components/button"
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@zenoxai/ui/components/popover"
<Popover>
  <PopoverTrigger render={<Button variant="outline" />}>Snooze</PopoverTrigger>
  <PopoverContent>
    <PopoverHeader>
      <PopoverTitle>Snooze alert</PopoverTitle>
      <PopoverDescription>Hide it from the queue.</PopoverDescription>
    </PopoverHeader>
    {/* controls */}
  </PopoverContent>
</Popover>

A popover opens on click next to its trigger and closes on Escape or a click outside. It is not modal: the rest of the page stays usable. Use it for a few related controls that belong to one button (snooze options, a “save view” field, a quick filter) or for an explanation the user asks for.

Need Use
A list of actions or options to pick from Dropdown Menu
A task that needs the user’s full attention, or more than a couple of fields Dialog
A one-line label for an icon button Tooltip
A preview of what a link points to, on hover Hover Card

PopoverContent is 18rem wide, opens below the trigger with its center aligned, and lays out its children in a column with a small gap. Change the width with className, the position with side, align, sideOffset and alignOffset.

Examples

With a form

Keep a popover form to one or two fields and a single action. Anything longer belongs in a Dialog or a Sheet.

<Popover>
  <PopoverTrigger render={<Button variant="outline" />}>
    Save view
  </PopoverTrigger>
  <PopoverContent align="start">
    <PopoverHeader>
      <PopoverTitle>Save view</PopoverTitle>
      <PopoverDescription>
        Keeps the current filters and columns under a name.
      </PopoverDescription>
    </PopoverHeader>
    <Field>
      <FieldLabel htmlFor="view-name">Name</FieldLabel>
      <Input id="view-name" placeholder="Critical alerts, Acme Corp" />
    </Field>
    <Button size="sm" className="self-end">
      Save view
    </Button>
  </PopoverContent>
</Popover>

Side

side is bottom by default. The popover flips to the opposite side and shifts along the edge when the preferred position would overflow the viewport.

<>
  {(["top", "right", "bottom", "left"] as const).map((side) => (
    <Popover key={side}>
      <PopoverTrigger render={<Button variant="outline" />}>
        {side}
      </PopoverTrigger>
      <PopoverContent side={side} className="w-56">
        <PopoverDescription>
          Opens on the {side}. It flips to the opposite side when there is
          no room.
        </PopoverDescription>
      </PopoverContent>
    </Popover>
  ))}
</>

Open on hover

For an info icon that explains a figure, set openOnHover on the trigger. It still opens on click and keyboard, so touch and screen reader users can reach the text, which a Tooltip does not allow. delay sets the hover delay in milliseconds.

Risk score: 82
<div className="flex items-center gap-1.5">
  <span>Risk score: 82</span>
  <Popover>
    <PopoverTrigger
      openOnHover
      delay={200}
      render={
        <Button
          variant="ghost"
          size="icon-xs"
          aria-label="How the risk score is calculated"
        />
      }
    >
      <InfoIcon />
    </PopoverTrigger>
    <PopoverContent side="top">
      <PopoverHeader>
        <PopoverTitle>Risk score</PopoverTitle>
        <PopoverDescription>
          Combines the severity of open alerts, exposed vulnerabilities and
          how critical the asset is. Above 70 is high.
        </PopoverDescription>
      </PopoverHeader>
    </PopoverContent>
  </Popover>
</div>

Accessibility

  • The trigger gets aria-expanded and aria-haspopup. Focus moves into the popover when it opens and returns to the trigger when it closes.
  • PopoverTitle and PopoverDescription label and describe the popup. Use them instead of plain text when the popover has a heading.
  • An icon-only trigger needs an aria-label that says what the popover holds.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

Popover

Popover is a floating panel anchored to its PopoverTrigger and opened on click, for interactive content that does not need a page of its own: a filter form, a date picker, quick settings. For a short hint on hover use Tooltip; for a list of actions, DropdownMenu; for a task that must hold the user's focus, Dialog.

PropTypeDefault
actionsRefRefObject<PopoverRootActions | null>

A ref to imperative actions. - `unmount`: Ends the closing phase of the popover after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the popover completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the popover imperatively when called.

-
defaultOpenboolean

Whether the popover is initially open.

false
defaultTriggerIdstring | null

ID of the trigger that the popover is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open popover.

-
handlePopoverHandle<unknown>

A handle to associate the popover with a trigger. If specified, allows external triggers to control the popover's open state.

-
modalboolean | "trap-focus"

Determines if the popover enters a modal state when open. - `true`: user interaction is limited to the popover: document page scroll is locked, and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. - `'trap-focus'`: focus is trapped inside the popover, but document page scroll is not locked and pointer interactions outside of it remain enabled.

false
onOpenChange((open: boolean, eventDetails: PopoverRootChangeEventDetails) => void)

Event handler called when the popover is opened or closed.

-
onOpenChangeComplete((open: boolean) => void)

Event handler called after any animations complete when the popover is opened or closed.

-
openboolean

Whether the popover is currently open.

-
triggerIdstring | null

ID of the trigger that the popover is associated with. This is useful in conjunction with the `open` prop to create a controlled popover. There's no need to specify this prop when the popover is uncontrolled (that is, when the `open` prop is not set).

-

PopoverContent

PopoverContent is the panel, 288px wide, in a portal below the trigger.

PropTypeDefault
align"center" | "start" | "end"

How to align the popup relative to the specified side.

start
alignOffsetnumber | OffsetFunction

Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment.

0
finalFocusboolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the popover is closed.

-
initialFocusboolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the popover is opened. By default, focus moves to the first tabbable element inside the popup, except when the popover is opened by touch — then the popup itself is focused to avoid opening the virtual keyboard.

-
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverPopupState>

Allows you to replace the component's HTML element with a different tag, or compose it with another component.

-
side"left" | "right" | "top" | "bottom" | "inline-end" | "inline-start"

Which side of the anchor element to align the popup against. May automatically change to avoid collisions.

bottom
sideOffsetnumber | OffsetFunction

Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment.

0

PopoverDescription

PropTypeDefault
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverDescriptionState>

Allows you to replace the component's HTML element with a different tag, or compose it with another component.

-

PopoverHeader

PopoverHeader stacks a PopoverTitle and a PopoverDescription at the top.

No props of its own. Accepts the props of the element it renders.

PopoverTitle

PopoverTitle names the panel, at the compact title step (text-md, semibold): the panel's text and controls sit at 12.5px, so the title needs the larger size and the heavier weight to stand apart.

PropTypeDefault
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTitleState>

Allows you to replace the component's HTML element with a different tag, or compose it with another component.

-

PopoverTrigger

PropTypeDefault
closeDelaynumber

How long to wait before closing the popover that was opened on hover. Specified in milliseconds.

0
delaynumber

How long to wait before the popover may be opened on hover. Specified in milliseconds.

300
handlePopoverHandle<unknown>

A handle to associate the trigger with a popover.

-
nativeButtonboolean

Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).

true
openOnHoverboolean

Whether the popover should also open when the trigger is hovered.

false
payloadunknown

A payload to pass to the popover when it is opened.

-
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTriggerState>

Allows you to replace the component's HTML element with a different tag, or compose it with another component.

-