Skip to main content
A popover is a floating container that displays rich content anchored to a trigger element. It’s useful for displaying additional information, menus, or interactive controls without cluttering the main interface.

Installation

npx shadcn@latest add @eo-n/popover

Usage

Import all parts and piece them together:
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover";
<Popover>
  <PopoverTrigger>Open</PopoverTrigger>
  <PopoverContent>
    <PopoverTitle>Popover Title</PopoverTitle>
    <PopoverDescription>
      This is some content inside the popover.
    </PopoverDescription>
  </PopoverContent>
</Popover>

Examples

Basic Popover

import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";

export default function PopoverDemo() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button>Open Popover</Button>
      </PopoverTrigger>
      <PopoverContent>
        <PopoverTitle>Popover Title</PopoverTitle>
        <PopoverDescription>
          This is some content inside the popover.
        </PopoverDescription>
      </PopoverContent>
    </Popover>
  );
}

With Form

Use a popover to contain a form:
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

export default function PopoverForm() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button>Settings</Button>
      </PopoverTrigger>
      <PopoverContent className="w-80">
        <div className="grid gap-4">
          <div className="space-y-2">
            <h4 className="font-medium leading-none">Dimensions</h4>
            <p className="text-sm text-muted-foreground">
              Set the dimensions for the layer.
            </p>
          </div>
          <div className="grid gap-2">
            <div className="grid grid-cols-3 items-center gap-4">
              <Label htmlFor="width">Width</Label>
              <Input
                id="width"
                defaultValue="100%"
                className="col-span-2 h-8"
              />
            </div>
            <div className="grid grid-cols-3 items-center gap-4">
              <Label htmlFor="maxWidth">Max. width</Label>
              <Input
                id="maxWidth"
                defaultValue="300px"
                className="col-span-2 h-8"
              />
            </div>
            <div className="grid grid-cols-3 items-center gap-4">
              <Label htmlFor="height">Height</Label>
              <Input
                id="height"
                defaultValue="25px"
                className="col-span-2 h-8"
              />
            </div>
          </div>
        </div>
      </PopoverContent>
    </Popover>
  );
}

Open on Hover

Trigger the popover on hover instead of click:
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";

export default function PopoverHover() {
  return (
    <Popover openOnHover>
      <PopoverTrigger asChild>
        <Button variant="outline">Hover me</Button>
      </PopoverTrigger>
      <PopoverContent>
        <PopoverTitle>Hover Popover</PopoverTitle>
        <PopoverDescription>
          This popover appears when you hover over the trigger.
        </PopoverDescription>
      </PopoverContent>
    </Popover>
  );
}

Controlled Popover

Control the popover’s open state:
import * as React from "react";
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";

export default function ControlledPopover() {
  const [open, setOpen] = React.useState(false);

  return (
    <div className="space-y-4">
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <Button>Open Popover</Button>
        </PopoverTrigger>
        <PopoverContent>
          <PopoverTitle>Controlled Popover</PopoverTitle>
          <PopoverDescription>
            This popover's state is controlled by React state.
          </PopoverDescription>
          <Button className="mt-4" onClick={() => setOpen(false)}>
            Close
          </Button>
        </PopoverContent>
      </Popover>
      <p className="text-sm text-muted-foreground">
        Popover is {open ? "open" : "closed"}
      </p>
    </div>
  );
}

Different Sides

Position the popover on different sides:
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";

export default function PopoverSides() {
  return (
    <div className="flex gap-4">
      <Popover>
        <PopoverTrigger asChild>
          <Button>Top</Button>
        </PopoverTrigger>
        <PopoverContent side="top">
          <PopoverTitle>Top Popover</PopoverTitle>
          <PopoverDescription>Positioned on top</PopoverDescription>
        </PopoverContent>
      </Popover>

      <Popover>
        <PopoverTrigger asChild>
          <Button>Bottom</Button>
        </PopoverTrigger>
        <PopoverContent side="bottom">
          <PopoverTitle>Bottom Popover</PopoverTitle>
          <PopoverDescription>Positioned on bottom</PopoverDescription>
        </PopoverContent>
      </Popover>

      <Popover>
        <PopoverTrigger asChild>
          <Button>Left</Button>
        </PopoverTrigger>
        <PopoverContent side="left">
          <PopoverTitle>Left Popover</PopoverTitle>
          <PopoverDescription>Positioned on left</PopoverDescription>
        </PopoverContent>
      </Popover>

      <Popover>
        <PopoverTrigger asChild>
          <Button>Right</Button>
        </PopoverTrigger>
        <PopoverContent side="right">
          <PopoverTitle>Right Popover</PopoverTitle>
          <PopoverDescription>Positioned on right</PopoverDescription>
        </PopoverContent>
      </Popover>
    </div>
  );
}

API Reference

Popover

The root component that manages the popover state.
open
boolean
Controls the open state of the popover.
defaultOpen
boolean
The initial open state for uncontrolled usage.
onOpenChange
(open: boolean) => void
Callback fired when the open state changes.
openOnHover
boolean
default:"false"
Opens the popover when hovering over the trigger.

PopoverTrigger

The button that toggles the popover.
asChild
boolean
default:"false"
Merge props onto the child element instead of wrapping it.

PopoverContent

The content to be rendered in the popover.
side
'top' | 'right' | 'bottom' | 'left'
default:"'bottom'"
The preferred side of the trigger to render against when open.
sideOffset
number
default:"4"
The distance in pixels from the trigger.
align
'start' | 'center' | 'end'
default:"'center'"
The preferred alignment against the trigger.
alignOffset
number
default:"0"
An offset in pixels from the alignment.
className
string
Additional CSS classes to apply.

PopoverHeader

Wrapper for the popover title and description.
className
string
Additional CSS classes to apply.

PopoverTitle

The accessible title of the popover.
className
string
Additional CSS classes to apply.

PopoverDescription

The accessible description of the popover.
className
string
Additional CSS classes to apply.

PopoverClose

A button that closes the popover.
asChild
boolean
default:"false"
Merge props onto the child element instead of wrapping it.

PopoverCreateHandle

Creates a handle for imperative popover control.
const handleRef = PopoverCreateHandle();

// Later, use the handle to control the popover
handleRef.current?.open();
handleRef.current?.close();

Build docs developers (and LLMs) love