# Popover
URL: /design/components/popover

Anchored detail on demand, dismissed by clicking away.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

\[interactive preview component PopoverSpecimen omitted]

Code for PopoverSpecimen preview:

```tsx
"use client";

import { useState, type ReactNode } from "react";
import { Button } from "@/components/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover";

function PopoverSpecimen(): ReactNode {
  return (
    <Popover>
      <PopoverTrigger
        render={<Button variant="outline">Context usage</Button>}
      />
      <PopoverContent className="w-64">
        <PopoverHeader>
          <PopoverTitle>Context usage</PopoverTitle>
          <PopoverDescription>
            12.4k of 200k tokens used in this thread.
          </PopoverDescription>
        </PopoverHeader>
      </PopoverContent>
    </Popover>
  );
}
```

## Installation

Install the dependencies:

- packages

  - @base-ui/react

Copy the source into `components/ui/popover.tsx`. Registry items that depend on it install it automatically.

- code

  "use client"; import \* as React from "react"; import { Popover as PopoverPrimitive } from "@base-ui/react/popover"; import { cn } from "@/lib/utils"; function Popover({ ...props }: PopoverPrimitive.Root.Props) { return \<PopoverPrimitive.Root data-slot="popover" {...props} />; } function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) { return \<PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />; } const PopoverContent = React.forwardRef< HTMLDivElement, PopoverPrimitive.Popup.Props & Pick< PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" > >(function PopoverContent( { className, align = "center", alignOffset = 0, side = "bottom", sideOffset = 4, ...props }, ref, ) { return ( \<PopoverPrimitive.Portal> \<PopoverPrimitive.Positioner align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} className="isolate z-50" > \<PopoverPrimitive.Popup ref={ref} data-slot="popover-content" className={cn( "bg-popover text-popover-foreground ring-foreground/10 data-\[side=bottom]:slide-in-from-top-2 data-\[side=inline-end]:slide-in-from-left-2 data-\[side=inline-start]:slide-in-from-right-2 data-\[side=left]:slide-in-from-right-2 data-\[side=right]:slide-in-from-left-2 data-\[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-\[var(--radius-surface,var(--radius-lg))] p-3 text-sm ring-1 outline-hidden duration-100", className, )} {...props} /> \</PopoverPrimitive.Positioner> \</PopoverPrimitive.Portal> ); }); function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="popover-header" className={cn("flex flex-col gap-0.5 text-sm", className)} {...props} /> ); } function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) { return ( \<PopoverPrimitive.Title data-slot="popover-title" className={cn("font-medium", className)} {...props} /> ); } function PopoverDescription({ className, ...props }: PopoverPrimitive.Description.Props) { return ( \<PopoverPrimitive.Description data-slot="popover-description" className={cn("text-muted-foreground", className)} {...props} /> ); } export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, };

* title

  components/ui/popover.tsx

* copyText

  "use client"; import \* as React from "react"; import { Popover as PopoverPrimitive } from "@base-ui/react/popover"; import { cn } from "@/lib/utils"; function Popover({ ...props }: PopoverPrimitive.Root.Props) { return \<PopoverPrimitive.Root data-slot="popover" {...props} />; } function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) { return \<PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />; } const PopoverContent = React.forwardRef< HTMLDivElement, PopoverPrimitive.Popup.Props & Pick< PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" > >(function PopoverContent( { className, align = "center", alignOffset = 0, side = "bottom", sideOffset = 4, ...props }, ref, ) { return ( \<PopoverPrimitive.Portal> \<PopoverPrimitive.Positioner align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} className="isolate z-50" > \<PopoverPrimitive.Popup ref={ref} data-slot="popover-content" className={cn( "bg-popover text-popover-foreground ring-foreground/10 data-\[side=bottom]:slide-in-from-top-2 data-\[side=inline-end]:slide-in-from-left-2 data-\[side=inline-start]:slide-in-from-right-2 data-\[side=left]:slide-in-from-right-2 data-\[side=right]:slide-in-from-left-2 data-\[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-\[var(--radius-surface,var(--radius-lg))] p-3 text-sm ring-1 outline-hidden duration-100", className, )} {...props} /> \</PopoverPrimitive.Positioner> \</PopoverPrimitive.Portal> ); }); function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="popover-header" className={cn("flex flex-col gap-0.5 text-sm", className)} {...props} /> ); } function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) { return ( \<PopoverPrimitive.Title data-slot="popover-title" className={cn("font-medium", className)} {...props} /> ); } function PopoverDescription({ className, ...props }: PopoverPrimitive.Description.Props) { return ( \<PopoverPrimitive.Description data-slot="popover-description" className={cn("text-muted-foreground", className)} {...props} /> ); } export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, };

* viewportClassName

  max-h-\[450px]

- language

  tsx

- code

  "use client"; import \* as React from "react"; import { Popover as PopoverPrimitive } from "@base-ui/react/popover"; import { cn } from "@/lib/utils"; function Popover({ ...props }: PopoverPrimitive.Root.Props) { return \<PopoverPrimitive.Root data-slot="popover" {...props} />; } function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) { return \<PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />; } const PopoverContent = React.forwardRef< HTMLDivElement, PopoverPrimitive.Popup.Props & Pick< PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" > >(function PopoverContent( { className, align = "center", alignOffset = 0, side = "bottom", sideOffset = 4, ...props }, ref, ) { return ( \<PopoverPrimitive.Portal> \<PopoverPrimitive.Positioner align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} className="isolate z-50" > \<PopoverPrimitive.Popup ref={ref} data-slot="popover-content" className={cn( "bg-popover text-popover-foreground ring-foreground/10 data-\[side=bottom]:slide-in-from-top-2 data-\[side=inline-end]:slide-in-from-left-2 data-\[side=inline-start]:slide-in-from-right-2 data-\[side=left]:slide-in-from-right-2 data-\[side=right]:slide-in-from-left-2 data-\[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-\[var(--radius-surface,var(--radius-lg))] p-3 text-sm ring-1 outline-hidden duration-100", className, )} {...props} /> \</PopoverPrimitive.Positioner> \</PopoverPrimitive.Portal> ); }); function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) { return ( \<div data-slot="popover-header" className={cn("flex flex-col gap-0.5 text-sm", className)} {...props} /> ); } function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) { return ( \<PopoverPrimitive.Title data-slot="popover-title" className={cn("font-medium", className)} {...props} /> ); } function PopoverDescription({ className, ...props }: PopoverPrimitive.Description.Props) { return ( \<PopoverPrimitive.Description data-slot="popover-description" className={cn("text-muted-foreground", className)} {...props} /> ); } export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, };

## Usage

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

export function Example() {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="outline">Details</Button>} />
      <PopoverContent>
        <PopoverHeader>
          <PopoverTitle>Context usage</PopoverTitle>
          <PopoverDescription>12.4k of 200k tokens.</PopoverDescription>
        </PopoverHeader>
      </PopoverContent>
    </Popover>
  );
}
```

## API Reference

The parts wrap Base UI's Popover primitives and accept their props.

### Popover

- `open?`: `boolean` — Controlled open state.
- `onOpenChange?`: `(open: boolean) => void` — Called when the open state changes.

### PopoverContent

- `side`: `"top" | "right" | "bottom" | "left"` (default `"bottom"`) — Preferred side of the trigger.
- `align`: `"start" | "center" | "end"` (default `"center"`) — Alignment against the trigger.
- `sideOffset`: `number` (default `4`) — Distance from the trigger in pixels.