---
type: doc
title: Popover
description: Displays rich content in a floating layer, triggered by a button.
---

> Fulldev UI for Astro. The `@fulldev` registry in `components.json` points at `https://ui.full.dev/r/styles/{style}/{name}.json`; see [installation](/astro/docs/installation.md).

```astro title="src/components/examples/popover/popover-demo.astro"
---
import { buttonVariants } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"
---

<Popover>
  <PopoverTrigger class={buttonVariants({ variant: "outline" })}>
    Open popover
  </PopoverTrigger>
  <PopoverContent class="w-80">
    <div class="grid gap-4">
      <div class="space-y-2">
        <h4 class="leading-none font-medium">Dimensions</h4>
        <p class="text-sm text-muted-foreground">
          Set the dimensions for the layer.
        </p>
      </div>
      <div class="grid gap-2">
        <div class="grid grid-cols-3 items-center gap-4">
          <Label for="width">Width</Label>
          <Input id="width" value="100%" class="col-span-2 h-8" />
        </div>
        <div class="grid grid-cols-3 items-center gap-4">
          <Label for="maxWidth">Max. width</Label>
          <Input id="maxWidth" value="300px" class="col-span-2 h-8" />
        </div>
        <div class="grid grid-cols-3 items-center gap-4">
          <Label for="height">Height</Label>
          <Input id="height" value="25px" class="col-span-2 h-8" />
        </div>
        <div class="grid grid-cols-3 items-center gap-4">
          <Label for="maxHeight">Max. height</Label>
          <Input id="maxHeight" value="none" class="col-span-2 h-8" />
        </div>
      </div>
    </div>
  </PopoverContent>
</Popover>
```

## Installation

```bash
npx shadcn@latest add @fulldev/popover
```

## Usage

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

```astro
<Popover>
  <PopoverTrigger class={buttonVariants({ variant: "outline" })}>
    Open Popover
  </PopoverTrigger>
  <PopoverContent>
    <PopoverHeader>
      <PopoverTitle>Title</PopoverTitle>
      <PopoverDescription>Description text here.</PopoverDescription>
    </PopoverHeader>
  </PopoverContent>
</Popover>
```

`PopoverTrigger` is a plain `button`: style it with `buttonVariants`, or give
another button `data-slot="popover-trigger"` inside `Popover`. Add
`data-slot="popover-close"` to a button inside the content to close it.

## Composition

```text
Popover
├── PopoverTrigger
└── PopoverContent
    └── PopoverHeader
        ├── PopoverTitle
        └── PopoverDescription
```

## Basic

A simple popover with a header, title, and description.

```astro title="src/components/examples/popover/popover-basic.astro"
---
import { buttonVariants } from "@/components/ui/button"
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"
---

<Popover>
  <PopoverTrigger
    class={buttonVariants({ variant: "outline", class: "w-fit" })}
  >
    Open Popover
  </PopoverTrigger>
  <PopoverContent align="start">
    <PopoverHeader>
      <PopoverTitle>Dimensions</PopoverTitle>
      <PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
    </PopoverHeader>
  </PopoverContent>
</Popover>
```

## Align

Use the `align` prop on `PopoverContent` to control the horizontal alignment.

```astro title="src/components/examples/popover/popover-alignments.astro"
---
import { buttonVariants } from "@/components/ui/button"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"
---

<div class="flex gap-6">
  <Popover>
    <PopoverTrigger class={buttonVariants({ variant: "outline", size: "sm" })}>
      Start
    </PopoverTrigger>
    <PopoverContent align="start" class="w-40">
      Aligned to start
    </PopoverContent>
  </Popover>
  <Popover>
    <PopoverTrigger class={buttonVariants({ variant: "outline", size: "sm" })}>
      Center
    </PopoverTrigger>
    <PopoverContent align="center" class="w-40">
      Aligned to center
    </PopoverContent>
  </Popover>
  <Popover>
    <PopoverTrigger class={buttonVariants({ variant: "outline", size: "sm" })}>
      End
    </PopoverTrigger>
    <PopoverContent align="end" class="w-40">
      Aligned to end
    </PopoverContent>
  </Popover>
</div>
```

## With Form

A popover with form fields inside.

```astro title="src/components/examples/popover/popover-form.astro"
---
import { buttonVariants } from "@/components/ui/button"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"
---

<Popover>
  <PopoverTrigger class={buttonVariants({ variant: "outline" })}>
    Open Popover
  </PopoverTrigger>
  <PopoverContent class="w-64" align="start">
    <PopoverHeader>
      <PopoverTitle>Dimensions</PopoverTitle>
      <PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
    </PopoverHeader>
    <FieldGroup class="gap-4">
      <Field orientation="horizontal">
        <FieldLabel for="form-width" class="w-1/2">
          Width
        </FieldLabel>
        <Input id="form-width" value="100%" />
      </Field>
      <Field orientation="horizontal">
        <FieldLabel for="form-height" class="w-1/2">
          Height
        </FieldLabel>
        <Input id="form-height" value="25px" />
      </Field>
    </FieldGroup>
  </PopoverContent>
</Popover>
```

## RTL

`side` takes physical sides only, so this example resolves `inline-start` and
`inline-end` for the text direction.

```astro title="src/components/examples/popover/popover-rtl.astro"
---
import { buttonVariants } from "@/components/ui/button"
import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"

const translations = {
  en: {
    dir: "ltr",
    values: {
      title: "Dimensions",
      description: "Set the dimensions for the layer.",
      "inline-start": "Inline Start",
      left: "Left",
      top: "Top",
      bottom: "Bottom",
      right: "Right",
      "inline-end": "Inline End",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      title: "الأبعاد",
      description: "تعيين الأبعاد للطبقة.",
      "inline-start": "بداية السطر",
      left: "يسار",
      top: "أعلى",
      bottom: "أسفل",
      right: "يمين",
      "inline-end": "نهاية السطر",
    },
  },
  he: {
    dir: "rtl",
    values: {
      title: "מימדים",
      description: "הגדר את המימדים לשכבה.",
      "inline-start": "תחילת השורה",
      left: "שמאל",
      top: "למעלה",
      bottom: "למטה",
      right: "ימין",
      "inline-end": "סוף השורה",
    },
  },
}

const physicalSides = ["left", "top", "bottom", "right"] as const
const logicalSides = ["inline-start", "inline-end"] as const

const { dir, values: t } = translations.ar

// PopoverContent takes physical sides, so resolve the logical ones for `dir`.
const resolvedSides = {
  "inline-start": dir === "rtl" ? "right" : "left",
  "inline-end": dir === "rtl" ? "left" : "right",
} as const
---

<div class="grid gap-4">
  <div class="flex flex-wrap justify-center gap-2">
    {physicalSides.map((side) => (
      <Popover>
        <PopoverTrigger class={buttonVariants({ variant: "outline" })}>
          {t[side]}
        </PopoverTrigger>
        <PopoverContent side={side} dir={dir}>
          <PopoverHeader>
            <PopoverTitle>{t.title}</PopoverTitle>
            <PopoverDescription>{t.description}</PopoverDescription>
          </PopoverHeader>
        </PopoverContent>
      </Popover>
    ))}
  </div>
  <div class="flex flex-wrap justify-center gap-2">
    {logicalSides.map((side) => (
      <Popover>
        <PopoverTrigger class={buttonVariants({ variant: "outline" })}>
          {t[side]}
        </PopoverTrigger>
        <PopoverContent side={resolvedSides[side]} dir={dir}>
          <PopoverHeader>
            <PopoverTitle>{t.title}</PopoverTitle>
            <PopoverDescription>{t.description}</PopoverDescription>
          </PopoverHeader>
        </PopoverContent>
      </Popover>
    ))}
  </div>
</div>
```

## API Reference

Source: [`src/components/ui/popover`](https://github.com/fulldotdev/ui/tree/main/src/components/ui/popover)

Behavior comes from [`@data-slot/popover`](https://github.com/bejamas/data-slot/blob/main/packages/popover/README.md).

### Popover

| Prop                  | Type      | Default |
| --------------------- | --------- | ------- |
| `defaultOpen`         | `boolean` | `false` |
| `portal`              | `boolean` | `true`  |
| `closeOnClickOutside` | `boolean` | `true`  |
| `closeOnEscape`       | `boolean` | `true`  |

The positioning props of `PopoverContent` also work on `Popover`.

### PopoverContent

| Prop               | Type                                     | Default    |
| ------------------ | ---------------------------------------- | ---------- |
| `side`             | `"top" \| "right" \| "bottom" \| "left"` | `"bottom"` |
| `align`            | `"start" \| "center" \| "end"`           | `"center"` |
| `sideOffset`       | `number`                                 | `4`        |
| `alignOffset`      | `number`                                 | `0`        |
| `avoidCollisions`  | `boolean`                                | `true`     |
| `collisionPadding` | `number`                                 | `8`        |

### Events

Listen for `popover:change` on the `Popover` root, and dispatch
`popover:set` with `{ open: boolean }` to open or close it from a script.

## Install what this page shows

```bash
npx shadcn@latest add @fulldev/popover-examples
```
