---
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
npx shadcn@latest add @fulldev/popover
Usage
import { buttonVariants } from "@/components/ui/button"
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover"
<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
Popover
├── PopoverTrigger
└── PopoverContent
└── PopoverHeader
├── PopoverTitle
└── PopoverDescription
Basic
A simple popover with a header, title, and description.
---
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.
---
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.
---
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.
---
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
Behavior comes from @data-slot/popover.
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.