Skip to main content
The Popover component displays floating content positioned relative to a trigger element, useful for additional options, forms, or information.

Basic Usage

import reflex_ui as ui

ui.popover(
    trigger=ui.button("Open Popover"),
    content="Popover content here"
)

Props Reference

trigger
Component
Component that opens the popover
content
str | Component
Main popover content
title
str | Component
Optional heading
description
str | Component
Optional descriptive text

With Title and Description

ui.popover(
    trigger=ui.button("Help"),
    title="Need Help?",
    description="Here's some guidance",
    content=rx.text("Detailed help content")
)

Controlled State

class State(rx.State):
    popover_open: bool = False

ui.popover(
    trigger=ui.button("Toggle"),
    content="Controlled popover",
    open=State.popover_open,
    on_open_change=State.set_popover_open
)
open
bool | Var[bool]
Whether popover is open (controlled)
default_open
bool
default:"False"
Initial open state (uncontrolled)
on_open_change
EventHandler[bool]
Event fired when popover opens or closes
on_open_change_complete
EventHandler[bool]
Event fired after animation completes

Positioning

side
Literal['top', 'right', 'bottom', 'left', 'inline-start', 'inline-end']
default:"bottom"
Which side of trigger to position popover
align
Literal['start', 'center', 'end']
default:"center"
How to align popover relative to trigger
side_offset
int
default:"4"
Distance between trigger and popover in pixels
align_offset
int
default:"0"
Offset along the alignment axis
ui.popover(
    trigger=ui.button("Above"),
    content="Appears above button",
    side="top",
    align="start",
    side_offset=8
)

Hover Behavior

open_on_hover
bool
default:"False"
Opens popover when hovering over trigger
delay
int
default:"300"
Delay before opening on hover (milliseconds)
close_delay
int
default:"0"
Delay before closing when hover ends (milliseconds)
ui.popover(
    trigger=ui.button("Hover Me"),
    content="Opens on hover",
    open_on_hover=True,
    delay=200
)
modal
bool | Literal['trap-focus']
default:"False"
  • True: Traps focus, locks scroll, blocks outside clicks
  • False: Allows page interaction (default for popover)
  • 'trap-focus': Only traps focus

Collision Handling

collision_padding
int | list[int]
default:"5"
Minimum distance from viewport edges
sticky
bool
default:"False"
Keep popover visible after anchor scrolls out of view
position_method
Literal['absolute', 'fixed']
default:"absolute"
CSS positioning strategy

Compositional API

ui.popover.root(
    ui.popover.trigger(
        render_=ui.button("Options")
    ),
    ui.popover.portal(
        ui.popover.positioner(
            ui.popover.popup(
                ui.popover.title("Options"),
                ui.popover.description("Choose an option"),
                rx.el.div(
                    ui.button("Option 1", variant="ghost"),
                    ui.button("Option 2", variant="ghost"),
                    class_name="flex flex-col gap-1"
                ),
                ui.popover.close(
                    render_=ui.button("Close", variant="outline")
                )
            ),
            side="right"
        )
    ),
    on_open_change=handle_change
)

Popover Components

popover.root

Container for all popover parts.
ui.popover.root(
    # child components
    modal=False
)

popover.trigger

Element that opens the popover.
ui.popover.trigger(
    render_=ui.button("Click Me"),
    open_on_hover=True
)

popover.portal

Portals popover to document body.
ui.popover.portal(
    ui.popover.positioner(...),
    container="#app-root"
)

popover.positioner

Positions the popover relative to trigger.
ui.popover.positioner(
    ui.popover.popup(...),
    side="top",
    align="end"
)

popover.popup

Main content container.
ui.popover.popup(
    "Content here",
    initial_focus="#first-input"
)
initial_focus
str
CSS selector of element to focus when opened
final_focus
str
CSS selector of element to focus when closed

popover.title

Heading (h2 element).
ui.popover.title("Popover Title")

popover.description

Description text (p element).
ui.popover.description("Additional context")

popover.close

Button to close popover.
ui.popover.close(
    render_=ui.button("Done")
)

popover.arrow

Arrow pointing to trigger.
ui.popover.arrow()  # Auto-positioned

popover.backdrop

Optional backdrop overlay.
ui.popover.backdrop()  # Only visible when modal=True

Styling Classes

Access predefined styles via ui.popover.class_names:
  • ROOT: Root element (empty)
  • TRIGGER: Trigger element (empty)
  • BACKDROP: Backdrop overlay (empty)
  • PORTAL: Portal element (empty)
  • POSITIONER: Positioner wrapper (empty)
  • POPUP: Popover container with border and shadow
  • ARROW: Arrow pointer
  • TITLE: Large semibold heading
  • DESCRIPTION: Secondary text
  • CLOSE: Close button (empty)

Animation

Smooth scale and opacity transitions:
  • Scales from 95% to 100%
  • Fades from 0 to 1 opacity
  • 150ms transition duration

Custom Anchor

anchor
str
CSS selector for custom anchor element (instead of trigger)
ui.popover(
    trigger=ui.button("Trigger"),
    content="Positioned relative to #custom",
    anchor="#custom-element"
)

Accessibility

  • Keyboard accessible (Escape to close)
  • Focus management
  • ARIA attributes
  • Screen reader support
  • Proper role semantics

Implementation Details

From source code at reflex_ui/components/base/popover.py:269:
  • Built on Base UI Popover primitives
  • High-level wrapper creates complete structure
  • Default side_offset of 4px
  • Minimum width of 9rem (144px)
  • Rounded corners and shadow styling
  • Flexible content with gap spacing

Build docs developers (and LLMs) love