Popover
Displays rich content in a portal triggered by a button or any custom element
Import
import { Popover } from '@heroui/react';Usage
"use client";
import {Button, Popover} from "@heroui/react";
export function PopoverBasic() {
  return (
    <div className="flex items-center gap-4">
      <Popover>
        <Button>Click me</Button>
        <Popover.Content>
          <Popover.Dialog>
            <Popover.Heading>Popover Title</Popover.Heading>
            <p className="text-muted mt-2 text-sm">
              This is the popover content. You can put any content here.
            </p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
    </div>
  );
}Anatomy
Import all parts and piece them together.
import { Popover } from '@heroui/react';
export default () => (
  <Popover>
    <Popover.Trigger/>
    <Popover.Content>
      <Popover.Arrow />
      <Popover.Dialog>
        <Popover.Heading/>
        {/* content goes here */}
      </Popover.Dialog>
    </Popover.Content>
  </Popover>
)With Arrow
"use client";
import {Button, Popover} from "@heroui/react";
import {Icon} from "@iconify/react";
export function PopoverWithArrow() {
  return (
    <div className="flex items-center gap-4">
      <Popover>
        <Button variant="secondary">With Arrow</Button>
        <Popover.Content>
          <Popover.Dialog>
            <Popover.Arrow />
            <Popover.Heading>Popover with Arrow</Popover.Heading>
            <p className="text-muted mt-2 text-sm">
              The arrow shows which element triggered the popover.
            </p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
      <Popover>
        <Button isIconOnly variant="tertiary">
          <Icon icon="gravity-ui:ellipsis" />
        </Button>
        <Popover.Content offset={10}>
          <Popover.Dialog>
            <Popover.Arrow />
            <Popover.Heading>Popover with Arrow</Popover.Heading>
            <p className="text-muted mt-2 text-sm">
              The arrow shows which element triggered the popover.
            </p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
    </div>
  );
}Placement
Click buttons
"use client";
import {Button, Popover} from "@heroui/react";
export function PopoverPlacement() {
  return (
    <div className="grid grid-cols-3 gap-4">
      <div />
      <Popover>
        <Button className="w-full" variant="tertiary">
          Top
        </Button>
        <Popover.Content placement="top">
          <Popover.Dialog>
            <Popover.Arrow />
            <p className="text-sm">Top placement</p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
      <div />
      <Popover>
        <Button className="w-full" variant="tertiary">
          Left
        </Button>
        <Popover.Content placement="left">
          <Popover.Dialog>
            <Popover.Arrow />
            <p className="text-sm">Left placement</p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
      <div className="flex items-center justify-center">
        <span className="text-muted text-sm">Click buttons</span>
      </div>
      <Popover>
        <Button className="w-full" variant="tertiary">
          Right
        </Button>
        <Popover.Content placement="right">
          <Popover.Dialog>
            <Popover.Arrow />
            <p className="text-sm">Right placement</p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
      <div />
      <Popover>
        <Button className="w-full" variant="tertiary">
          Bottom
        </Button>
        <Popover.Content placement="bottom">
          <Popover.Dialog>
            <Popover.Arrow />
            <p className="text-sm">Bottom placement</p>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
      <div />
    </div>
  );
}Interactive Content
Sarah Johnson
@sarahj
"use client";
import {Avatar, Button, Popover} from "@heroui/react";
import {useState} from "react";
export function PopoverInteractive() {
  const [isFollowing, setIsFollowing] = useState(false);
  return (
    <div className="flex items-center gap-6">
      <Popover>
        <Popover.Trigger aria-label="User profile">
          <div className="flex items-center gap-2">
            <Avatar size="sm">
              <Avatar.Image
                alt="Sarah Johnson"
                src="https://img.heroui.chat/image/avatar?w=400&h=400&u=3"
              />
              <Avatar.Fallback>SJ</Avatar.Fallback>
            </Avatar>
            <div className="flex flex-col">
              <p className="text-sm font-medium">Sarah Johnson</p>
              <p className="text-muted text-xs">@sarahj</p>
            </div>
          </div>
        </Popover.Trigger>
        <Popover.Content className="w-[320px]">
          <Popover.Dialog>
            <Popover.Heading>
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-3">
                  <Avatar size="md">
                    <Avatar.Image
                      alt="Sarah Johnson"
                      src="https://img.heroui.chat/image/avatar?w=400&h=400&u=3"
                    />
                    <Avatar.Fallback>SJ</Avatar.Fallback>
                  </Avatar>
                  <div>
                    <p className="font-semibold">Sarah Johnson</p>
                    <p className="text-muted text-sm">@sarahj</p>
                  </div>
                </div>
                <Button
                  className="rounded-full"
                  size="sm"
                  variant={isFollowing ? "tertiary" : "primary"}
                  onPress={() => setIsFollowing(!isFollowing)}
                >
                  {isFollowing ? "Following" : "Follow"}
                </Button>
              </div>
            </Popover.Heading>
            <p className="text-muted mt-3 text-sm">
              Product designer and creative director. Building beautiful experiences that matter.
            </p>
            <div className="mt-3 flex gap-4">
              <div>
                <span className="font-semibold">892</span>
                <span className="text-muted ml-1 text-sm">Following</span>
              </div>
              <div>
                <span className="font-semibold">12.5K</span>
                <span className="text-muted ml-1 text-sm">Followers</span>
              </div>
            </div>
          </Popover.Dialog>
        </Popover.Content>
      </Popover>
    </div>
  );
}Styling
Passing Tailwind CSS classes
import {Popover, Button} from '@heroui/react';
function CustomPopover() {
  return (
    <Popover>
      <Button>Open</Button>
      <Popover.Content className="bg-accent text-accent-foreground">
        <Popover.Dialog>
          <Popover.Heading>Custom Styled</Popover.Heading>
          <p>This popover has custom styling</p>
        </Popover.Dialog>
      </Popover.Content>
    </Popover>
  );
}Customizing the component classes
To customize the Popover component classes, you can use the @layer components directive.
Learn more.
@layer components {
  .popover {
    @apply rounded-xl shadow-2xl;
  }
  .popover__dialog {
    @apply p-4;
  }
  .popover__heading {
    @apply text-lg font-bold;
  }
}HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The Popover component uses these CSS classes (View source styles):
Base Classes
.popover- Base popover container styles.popover__dialog- Dialog content wrapper.popover__heading- Heading text styles.popover__trigger- Trigger element styles
Interactive States
The component supports animation states:
- Entering: 
[data-entering]- Applied during popover appearance - Exiting: 
[data-exiting]- Applied during popover disappearance - Placement: 
[data-placement="*"]- Applied based on popover position - Focus: 
:focus-visibleor[data-focus-visible="true"] 
API Reference
Popover Props
| Prop | Type | Default | Description | 
|---|---|---|---|
children | React.ReactNode | - | Trigger and content elements | 
isOpen | boolean | - | Controls popover visibility (controlled) | 
defaultOpen | boolean | false | Initial open state (uncontrolled) | 
onOpenChange | (isOpen: boolean) => void | - | Called when open state changes | 
Popover.Content Props
| Prop | Type | Default | Description | 
|---|---|---|---|
children | React.ReactNode | - | Content to display in the popover | 
placement | "top" | "bottom" | "left" | "right" (and variants) | "bottom" | Placement of the popover | 
offset | number | 8 | Distance from the trigger element | 
shouldFlip | boolean | true | Whether popover can change orientation to fit | 
className | string | - | Additional CSS classes | 
Popover.Dialog Props
| Prop | Type | Default | Description | 
|---|---|---|---|
children | React.ReactNode | - | Dialog content | 
className | string | - | Additional CSS classes | 
Popover.Trigger Props
| Prop | Type | Default | Description | 
|---|---|---|---|
children | React.ReactNode | - | Element that triggers the popover | 
className | string | - | Additional CSS classes | 
Popover.Heading Props
| Prop | Type | Default | Description | 
|---|---|---|---|
children | React.ReactNode | - | Heading text content | 
className | string | - | Additional CSS classes | 
Popover.Arrow Props
| Prop | Type | Default | Description | 
|---|---|---|---|
children | React.ReactNode | - | Custom arrow element | 
className | string | - | Additional CSS classes |