---
layout: ~/layouts/MdxDocLayout.astro
title: "Popover"
description: "An accessible popup anchored to a trigger element, used for displaying rich content like menus, forms, or additional information."
sourceFile: "components/popover"
baseUIComponent: "popover"
---

import ComponentExample from "~/components/docs/ComponentExample.astro";
import ComponentSection from "~/components/docs/ComponentSection.astro";
import Heading from "~/components/docs/Heading.astro";
import PropsTable from "~/components/docs/PropsTable.astro";
import {
  PopoverHeroDemo,
  PopoverBasicDemo,
  PopoverWithCloseDemo,
  PopoverPositionDemo,
  PopoverCustomContentDemo,
  PopoverOpenOnHoverDemo,
  PopoverVirtualAnchorDemo,
} from "~/components/demos/PopoverDemo";

{/* Hero Demo */}

<ComponentSection>
  <ComponentExample demo="PopoverHeroDemo">
    <PopoverHeroDemo client:load />
  </ComponentExample>
</ComponentSection>

{/* Installation */}

<ComponentSection>
  <Heading level={2}>Installation</Heading>
  <Heading level={3}>Barrel</Heading>

```tsx
import { Popover } from "@cloudflare/kumo";
```

<Heading level={3}>Granular</Heading>

```tsx
import { Popover } from "@cloudflare/kumo/components/popover";
```

</ComponentSection>

{/* Usage */}

<ComponentSection>
  <Heading level={2}>Usage</Heading>

```tsx
import { Popover, Button } from "@cloudflare/kumo";

export default function Example() {
  return (
    <Popover>
      <Popover.Trigger asChild>
        <Button>Open</Button>
      </Popover.Trigger>
      <Popover.Content>
        <Popover.Title>Popover Title</Popover.Title>
        <Popover.Description>Popover content goes here.</Popover.Description>
      </Popover.Content>
    </Popover>
  );
}
```

</ComponentSection>

{/* Popover vs Tooltip */}

<ComponentSection>
  <Heading level={2}>Popover vs Tooltip</Heading>
  <p>
    While popovers can be triggered on hover (using `openOnHover`), they serve a
    different purpose than tooltips. Understanding when to use each is important
    for accessibility and user experience.
  </p>
  <div class="overflow-hidden rounded-lg border border-kumo-hairline">
    <table class="w-full text-sm">
      <thead class="bg-kumo-elevated">
        <tr>
          <th class="px-4 py-3 text-left font-semibold"></th>
          <th class="px-4 py-3 text-left font-semibold">Tooltip</th>
          <th class="px-4 py-3 text-left font-semibold">Popover</th>
        </tr>
      </thead>
      <tbody class="divide-y divide-kumo-hairline">
        <tr>
          <td class="px-4 py-3 font-medium">Purpose</td>
          <td class="px-4 py-3 text-kumo-subtle">
            Short, non-interactive text labels for identification
          </td>
          <td class="px-4 py-3 text-kumo-subtle">
            Rich, interactive content containers
          </td>
        </tr>
        <tr>
          <td class="px-4 py-3 font-medium">Content</td>
          <td class="px-4 py-3 text-kumo-subtle">Plain text only</td>
          <td class="px-4 py-3 text-kumo-subtle">
            Any content: links, buttons, forms, images
          </td>
        </tr>
        <tr>
          <td class="px-4 py-3 font-medium">Trigger</td>
          <td class="px-4 py-3 text-kumo-subtle">Hover or focus</td>
          <td class="px-4 py-3 text-kumo-subtle">Click (default) or hover</td>
        </tr>
        <tr>
          <td class="px-4 py-3 font-medium">ARIA Role</td>
          <td class="px-4 py-3">
            <code class="rounded bg-kumo-elevated px-1.5 py-0.5 font-mono text-xs">
              role="tooltip"
            </code>
          </td>
          <td class="px-4 py-3">
            <code class="rounded bg-kumo-elevated px-1.5 py-0.5 font-mono text-xs">
              aria-haspopup
            </code>
          </td>
        </tr>
        <tr>
          <td class="px-4 py-3 font-medium">Keyboard</td>
          <td class="px-4 py-3 text-kumo-subtle">Not focusable</td>
          <td class="px-4 py-3 text-kumo-subtle">
            Focus moves inside, traps when open
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <p>
    <strong>Use a Tooltip</strong> when you need to label an icon button or
    provide a brief explanation. <strong>Use a Popover</strong> when users need
    to interact with the content inside, such as clicking links, filling out
    forms, or dismissing with a button.
  </p>
</ComponentSection>

{/* Examples */}

<ComponentSection>
  <Heading level={2}>Examples</Heading>

<Heading level={3}>Basic Popover</Heading>
<ComponentExample demo="PopoverBasicDemo">
  <PopoverBasicDemo client:load />
</ComponentExample>

<Heading level={3}>With Close Button</Heading>
<ComponentExample demo="PopoverWithCloseDemo">
  <PopoverWithCloseDemo client:load />
</ComponentExample>

<Heading level={3}>Positioning</Heading>
<p>
  Use the `side` prop to control where the popover appears relative to the
  trigger.
</p>
<ComponentExample demo="PopoverPositionDemo">
  <PopoverPositionDemo client:load />
</ComponentExample>

<Heading level={3}>Custom Content</Heading>
<p>
  Popovers can contain any content, including custom layouts with avatars,
  buttons, and more.
</p>
<ComponentExample demo="PopoverCustomContentDemo">
  <PopoverCustomContentDemo client:load />
</ComponentExample>

<Heading level={3}>Open on Hover</Heading>
<p>
  Use `openOnHover` on the trigger to open the popover when the user hovers over
  it. You can also specify a `delay` in milliseconds before the popover appears.
</p>
<ComponentExample demo="PopoverOpenOnHoverDemo">
  <PopoverOpenOnHoverDemo client:load />
</ComponentExample>

<Heading level={3}>Virtual Anchor</Heading>
<p>
  Use the `anchor` prop on `Popover.Content` to position the popover against an
  element other than the trigger, or against a virtual point (e.g., a `DOMRect`
  from `getBoundingClientRect()`). This is useful when the trigger and the
  desired anchor are in different component trees.
</p>
<ComponentExample demo="PopoverVirtualAnchorDemo">
  <PopoverVirtualAnchorDemo client:load />
</ComponentExample>

</ComponentSection>

{/* API Reference */}

<ComponentSection>
  <Heading level={2}>API Reference</Heading>

<Heading level={3}>Popover</Heading>
<p>The root component that manages the popover's open state.</p>
<PropsTable component="Popover" />

<Heading level={3}>Popover.Trigger</Heading>
<p>
  A button that opens the popover when clicked. Use `asChild` to render your own
  element.
</p>
<PropsTable component="Popover.Trigger" />

<Heading level={3}>Popover.Content</Heading>
<p>
  The container for popover content. Controls positioning via `side`, `align`,
  `sideOffset`, and `alignOffset` props. Use the `anchor` prop to position
  against a custom element or virtual point instead of the trigger. Use
  `positionMethod="fixed"` when the popover needs to escape stacking contexts,
  such as when inside sticky headers.
</p>
<PropsTable component="Popover.Content" />

<Heading level={3}>Popover.Title</Heading>
<p>A heading that labels the popover for accessibility.</p>
<PropsTable component="Popover.Title" />

<Heading level={3}>Popover.Description</Heading>
<p>A paragraph providing additional context about the popover content.</p>
<PropsTable component="Popover.Description" />

<Heading level={3}>Popover.Close</Heading>
<p>
  A button that closes the popover when clicked. Use `asChild` to render your
  own element.
</p>
<PropsTable component="Popover.Close" />

</ComponentSection>
