Popover

Displays rich content in a portal, triggered by a button.

Anatomy

Import and assemble the component:

1import { Popover } from "@raystack/apsara";
2
3<Popover>
4 <Popover.Trigger />
5 <Popover.Content />
6</Popover>

Usage

A popover anchors to its trigger. Where it lands is side and align; when it opens can be yours.

Positioning

Control the position and alignment of your popover relative to its trigger.

1<Popover>
2 <Popover.Trigger render={<Button />}>Top Popover</Popover.Trigger>
3 <Popover.Content side="top">
4 <Text size="small">Content appears above the trigger</Text>
5 </Popover.Content>
6</Popover>

Alignment

Customize how the popover aligns with its trigger.

1<Popover>
2 <Popover.Trigger render={<Button />}>Center Aligned</Popover.Trigger>
3 <Popover.Content align="center">
4 <Text size="small">Centered with the trigger</Text>
5 </Popover.Content>
6</Popover>

Controlled

Pass open with onOpenChange to own the state. Do this when something other than the trigger has to close the popover, such as applying a filter.

1(function ControlledPopover() {
2 const [open, setOpen] = React.useState(false);
3
4 return (
5 <Flex align="center" gap={5}>
6 <Popover open={open} onOpenChange={setOpen}>
7 <Popover.Trigger render={<Button variant="outline" />}>
8 Filters
9 </Popover.Trigger>
10 <Popover.Content>
11 <Flex direction="column" gap={4} style={{ padding: 4 }}>
12 <Text size="small">Two filters active.</Text>
13 <Button size="small" onClick={() => setOpen(false)}>
14 Apply

Shared popover

Create a handle with Popover.createHandle() and pass it to several triggers and one Popover. Each trigger sends its own payload. While the popover is open, clicking another trigger moves it there. Wrap the content in Popover.Viewport to fade and slide the old content out and the new content in.

1(function SharedPopover() {
2 const [handle] = React.useState(() => Popover.createHandle());
3
4 return (
5 <>
6 <Flex gap={3} align="center">
7 <Popover.Trigger
8 handle={handle}
9 payload="Two unread messages."
10 render={<Button variant="outline" />}
11 >
12 Inbox
13 </Popover.Trigger>
14 <Popover.Trigger

API Reference

A trigger, and the content it opens.

Root

Groups all parts of the popover.

Prop

Type

Content

Renders the popover content panel.

Prop

Type

Trigger

Renders the element that opens the popover.

Prop

Type

Viewport

Wraps the content inside Popover.Content and animates the swap when a shared popover moves to another trigger. Optional.

Prop

Type

Slots

Every rendered part carries a stable data-slot attribute for styling and testing:

SlotElement
popover-positionerPositioner that places the popup
popover-contentThe popover popup
popover-viewportThe Popover.Viewport element

Accessibility

  • Follows the WAI-ARIA Dialog pattern
  • Trigger uses aria-haspopup and aria-expanded attributes
  • Focus is managed when opening and closing the popover
  • Supports dismissal with Escape key
  • Respects motion preferences: popover entry motion is enabled only when prefers-reduced-motion: no-preference