Skip to main content

Overview

Popover displays floating content anchored to a trigger element. It includes smart positioning with collision detection, automatic focus management, and keyboard navigation.

Basic usage

import { Popover } from '@raystack/apsara';

<Popover>
  <Popover.Trigger asChild>
    <button>Open popover</button>
  </Popover.Trigger>
  
  <Popover.Content>
    <div>Popover content</div>
  </Popover.Content>
</Popover>

Components

Popover (Root)

The root component that manages popover state and positioning context.
open
boolean
Controls the open state of the popover (controlled mode).
defaultOpen
boolean
The initial open state in uncontrolled mode.
onOpenChange
(open: boolean) => void
Callback fired when the open state changes.
modal
boolean
default:"false"
Whether the popover is modal (blocks interaction with content behind it).
openOnHover
boolean
Whether to open the popover on hover instead of click.

Popover.Trigger

A button that opens the popover when activated.
asChild
boolean
When true, merges props with the immediate child element instead of rendering a button.

Popover.Content

The popover content container with positioning logic.
side
'top' | 'right' | 'bottom' | 'left'
default:"'bottom'"
The preferred side of the trigger to render the popover.
align
'start' | 'center' | 'end'
default:"'center'"
The preferred alignment against the trigger.
sideOffset
number
default:"4"
The distance in pixels from the trigger.
alignOffset
number
The offset in pixels from the aligned edge.
collisionPadding
number
default:"3"
The padding in pixels from the viewport edges for collision detection.
sticky
'partial' | 'always'
The sticky behavior of the popover during scrolling.
hideWhenDetached
boolean
Whether to hide the popover when the trigger is fully occluded.
initialFocus
number | React.RefObject
The element that receives focus when the popover opens.
finalFocus
React.RefObject
The element that receives focus when the popover closes.
className
string
Additional CSS classes for the popover content.

Popover.Close

A button that closes the popover when clicked.
asChild
boolean
When true, merges props with the immediate child element instead of rendering a button.

Usage examples

Positioned popover

<Popover>
  <Popover.Trigger asChild>
    <button>Open above</button>
  </Popover.Trigger>
  
  <Popover.Content side="top" align="start">
    <div>This popover appears above the trigger, aligned to the start.</div>
  </Popover.Content>
</Popover>

With custom offset

<Popover>
  <Popover.Trigger asChild>
    <button>Open popover</button>
  </Popover.Trigger>
  
  <Popover.Content sideOffset={12} alignOffset={8}>
    <div>Popover with custom offset values.</div>
  </Popover.Content>
</Popover>

Hover trigger

<Popover openOnHover>
  <Popover.Trigger asChild>
    <button>Hover me</button>
  </Popover.Trigger>
  
  <Popover.Content>
    <div>Opens on hover instead of click.</div>
  </Popover.Content>
</Popover>

Controlled popover

function ControlledPopover() {
  const [open, setOpen] = useState(false);
  
  return (
    <Popover open={open} onOpenChange={setOpen}>
      <Popover.Trigger asChild>
        <button>Toggle popover</button>
      </Popover.Trigger>
      
      <Popover.Content>
        <div>
          <p>Controlled popover content</p>
          <button onClick={() => setOpen(false)}>Close</button>
        </div>
      </Popover.Content>
    </Popover>
  );
}

With close button

<Popover>
  <Popover.Trigger asChild>
    <button>Open popover</button>
  </Popover.Trigger>
  
  <Popover.Content>
    <div>
      <h3>Popover title</h3>
      <p>Some content here.</p>
      <Popover.Close asChild>
        <button>Close</button>
      </Popover.Close>
    </div>
  </Popover.Content>
</Popover>
<Popover modal>
  <Popover.Trigger asChild>
    <button>Open modal popover</button>
  </Popover.Trigger>
  
  <Popover.Content>
    <div>This popover blocks interaction with content behind it.</div>
  </Popover.Content>
</Popover>

Positioning

The Popover uses smart positioning logic:
  1. Preferred side: Set via the side prop
  2. Collision detection: Automatically flips to the opposite side if there isn’t enough space
  3. Alignment: Control via the align prop (start, center, end)
  4. Offsets: Fine-tune position with sideOffset and alignOffset
All positioning is handled automatically with viewport boundary detection:
<Popover.Content
  side="bottom"
  align="start"
  sideOffset={8}
  collisionPadding={10}
/>

Accessibility features

  • Keyboard navigation: Arrow keys to navigate, Enter/Space to activate
  • ESC to close: Press Escape to close the popover
  • Focus management: Automatic focus handling with initialFocus and finalFocus props
  • Focus return: Focus returns to the trigger when closed
  • ARIA attributes: Proper ARIA relationships between trigger and content
  • Screen reader support: Announces popover state changes

Trigger patterns

The Popover.Trigger component supports two usage patterns:
  1. Default button: Renders a button when no children provided or asChild is false
  2. Custom trigger: Use asChild to merge popover functionality with your component
{/* Default button */}
<Popover.Trigger>Open</Popover.Trigger>

{/* Custom trigger */}
<Popover.Trigger asChild>
  <button className="custom-button">Open</button>
</Popover.Trigger>