Popover

Overlay & Feedback

Floating content panel anchored to a trigger.

Preview

Usage

example.jsx
import { Popover } from "@/components/ui/popover";

export default function Example() {
  return <Popover />;
}

Source Code

Copy this file into components/ui/popover.jsx in your project.

popover.jsx
"use client";

import { forwardRef, useState, useRef, useEffect } from "react";
import { cn } from "@/lib/utils";

const Popover = forwardRef(({ className, trigger, children, align = "center", ...props }, ref) => {
  const [open, setOpen] = useState(false);
  const popoverRef = useRef(null);

  useEffect(() => {
    const handler = (e) => { if (popoverRef.current && !popoverRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  return (
    <div ref={(el) => { popoverRef.current = el; if (typeof ref === "function") ref(el); else if (ref) ref.current = el; }} className={cn("relative inline-block", className)} {...props}>
      <div onClick={() => setOpen(!open)}>{trigger}</div>
      {open && (
        <div className={cn("absolute z-50 mt-2 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none animate-in fade-in-0 zoom-in-95",
          align === "start" && "left-0",
          align === "center" && "left-1/2 -translate-x-1/2",
          align === "end" && "right-0"
        )}>
          {children}
        </div>
      )}
    </div>
  );
});
Popover.displayName = "Popover";

export { Popover };

Quick Install

Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.

npm install clsx tailwind-merge