Popover

New

Overview

A floating panel for interactive content, opened by click, hover, or focus.

Put exactly one interactive element in trigger and interactive flyout controls in content. The hook adds the expanded-state and dialog relationships, then keeps the panel local to the browser for immediate interaction. Use Tooltip for non-interactive descriptions or contextual help.

HEEx
<div class="flex justify-center">
  <Popover.popover id="project-actions-popover" label="Project actions">
    <:trigger>
      <Button.button id="project-actions-trigger" kind={:outline}>Manage project</Button.button>
    </:trigger>
    <:content>
      <div class="grid gap-3">
        <h3 class="font-semibold">Website redesign</h3>
        <div class="flex flex-wrap gap-2">
          <Button.button
            size={:sm}
            phx-click="demo-action"
            phx-value-action="Editing website redesign"
          >
            Edit project
          </Button.button>
          <Button.button
            size={:sm}
            kind={:destructive}
            phx-click="demo-action"
            phx-value-action="Archive confirmation opened"
          >
            Archive
          </Button.button>
        </div>
      </div>
    </:content>
  </Popover.popover>
</div>

Open on hover

Set open_on_hover to reveal flyout actions during pointer exploration. The panel remains open while the pointer moves between the trigger and its interactive content, and clicking remains available for touch input.

HEEx
<div class="flex justify-center">
  <Popover.popover
    id="reviewer-popover"
    label="Reviewer details"
    side={:right}
    open_on_hover
  >
    <:trigger>
      <Button.button id="reviewer-popover-trigger" kind={:outline}>Maya Chen</Button.button>
    </:trigger>
    <:content>
      <div class="grid gap-3">
        <strong>Maya Chen</strong>
        <span class="text-muted-foreground">Design systems · Detroit</span>
        <div class="flex flex-wrap gap-2">
          <Button.button
            size={:sm}
            phx-click="demo-action"
            phx-value-action="Opening Maya Chen’s profile"
          >
            View profile
          </Button.button>
          <Button.button
            size={:sm}
            kind={:outline}
            phx-click="demo-action"
            phx-value-action="Starting a message to Maya Chen"
          >
            Message
          </Button.button>
        </div>
      </div>
    </:content>
  </Popover.popover>
</div>

Open on focus

Set open_on_focus to reveal interactive navigation flyouts when their triggers receive keyboard focus. Focus can move into each flyout’s links without closing it.

HEEx
<nav aria-label="Example navigation" class="flex flex-wrap justify-center gap-1">
  <Popover.popover id="product-nav" label="Product navigation" side={:bottom_start} open_on_focus>
    <:trigger>
      <Button.button id="product-nav-trigger" kind={:ghost}>Product</Button.button>
    </:trigger>
    <:content>
      <div class="grid gap-1">
        <strong class="mb-1">Product</strong>
        <Link.link navigate="/" class="rounded-lg px-2 py-1.5 hover:bg-muted hover:no-underline">Component catalog</Link.link>
        <Link.link navigate="/pricing" class="rounded-lg px-2 py-1.5 hover:bg-muted hover:no-underline">Pricing</Link.link>
      </div>
    </:content>
  </Popover.popover>
  <Popover.popover id="resources-nav" label="Resources navigation" side={:bottom_start} open_on_focus>
    <:trigger>
      <Button.button id="resources-nav-trigger" kind={:ghost}>Resources</Button.button>
    </:trigger>
    <:content>
      <div class="grid gap-1">
        <strong class="mb-1">Resources</strong>
        <Link.link navigate="/docs/installation" class="rounded-lg px-2 py-1.5 hover:bg-muted hover:no-underline">Installation</Link.link>
        <Link.link navigate="/docs/floating-ui" class="rounded-lg px-2 py-1.5 hover:bg-muted hover:no-underline">Floating UI</Link.link>
      </div>
    </:content>
  </Popover.popover>
  <Popover.popover id="company-nav" label="Company navigation" side={:bottom_start} open_on_focus>
    <:trigger>
      <Button.button id="company-nav-trigger" kind={:ghost}>Company</Button.button>
    </:trigger>
    <:content>
      <div class="grid gap-1">
        <strong class="mb-1">Company</strong>
        <Link.link navigate="/roadmap" class="rounded-lg px-2 py-1.5 hover:bg-muted hover:no-underline">Roadmap</Link.link>
        <Link.link navigate="/accessibility" class="rounded-lg px-2 py-1.5 hover:bg-muted hover:no-underline">Accessibility</Link.link>
      </div>
    </:content>
  </Popover.popover>
</nav>

Positioning options

Popover exposes the shared Floating UI options used by Tooltip, Dropdown, and Date Picker: side, offset, strategy, shift, placement_strategy, arrow, and hide.

HEEx
<div class="flex justify-center">
  <Popover.popover
    id="positioned-popover"
    label="Notification actions"
    side={:top_start}
    offset={16}
    strategy={:fixed}
    placement_strategy={:auto_placement}
    shift
    arrow
    hide
  >
    <:trigger><Button.button kind={:outline}>Notification actions</Button.button></:trigger>
    <:content>
      <div class="grid gap-2">
        <Button.button
          size={:sm}
          class="w-full"
          phx-click="demo-action"
          phx-value-action="All notifications marked as read"
        >
          Mark all as read
        </Button.button>
        <Button.button
          size={:sm}
          kind={:outline}
          class="w-full"
          phx-click="demo-action"
          phx-value-action="Opening notification settings"
        >
          Notification settings
        </Button.button>
      </div>
    </:content>
  </Popover.popover>
</div>

Programmatic control

Use Popover.show/1, Popover.hide/1, or Popover.toggle/1 inside a Phoenix JS command pipeline to control the same local browser state.

HEEx
<div class="flex flex-wrap items-center justify-center gap-2">
  <Button.button id="open-command-popover" phx-click={Popover.show("command-popover")}>Open popover</Button.button>
  <Button.button id="close-command-popover" kind={:outline} phx-click={Popover.hide("command-popover")}>Close popover</Button.button>
  <Popover.popover id="command-popover" label="Quick actions" side={:bottom}>
    <:trigger><Button.button id="command-popover-trigger" kind={:ghost}>Quick actions</Button.button></:trigger>
    <:content>
      <div class="grid gap-2">
        <Button.button
          size={:sm}
          kind={:ghost}
          class="w-full !justify-start"
          phx-click="demo-action"
          phx-value-action="Search opened"
        >
          Open search
        </Button.button>
        <Button.button
          size={:sm}
          kind={:ghost}
          class="w-full !justify-start"
          phx-click="demo-action"
          phx-value-action="New task started"
        >
          Create task
        </Button.button>
      </div>
    </:content>
  </Popover.popover>
</div>

Add this module

terminal
mix dizzy.add popover

Component API

Popover.popover

Attributes

Attributes for Popover.popover
NameTypeDefault Values
arrow :boolean false—
class :any nil—
hide :boolean false—
id :string Required—
label :string Required—
offset :integer 8—
open_on_focus :boolean false—
open_on_hover :boolean false—
placement_strategy :atom :flipnil, :flip, :auto_placement
shift :boolean true—
side :atom :bottom:top, :top_start, :top_end, :bottom, :bottom_start, :bottom_end, :left, :left_start, :left_end, :right, :right_start, :right_end
strategy :atom :fixed:absolute, :fixed

Slots

Slots for Popover.popover
NameRequired
content Required
trigger Required

JavaScript hook

DizzyPopover

Connects the explicit trigger and content slots, manages click, hover, focus, outside-click, and Escape behavior, and uses the shared Floating UI positioning layer for collision handling, arrows, and detached-reference hiding.

mix dizzy.add includes the hook and performs any required asset setup automatically.

Explore Dizzy