"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
export function PopoverDemo() {
return (
<Popover>
<PopoverTrigger render={<Button variant="tertiary" />}>
Open popover
</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-muted-foreground text-sm">
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="popover-width">Width</Label>
<Input
variant="secondary"
id="popover-width"
defaultValue="100%"
className="col-span-2 h-8"
/>
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="popover-max-width">Max. width</Label>
<Input
variant="secondary"
id="popover-max-width"
defaultValue="300px"
className="col-span-2 h-8"
/>
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="popover-height">Height</Label>
<Input
variant="secondary"
id="popover-height"
defaultValue="25px"
className="col-span-2 h-8"
/>
</div>
<div className="grid grid-cols-3 items-center gap-4">
<Label htmlFor="popover-max-height">Max. height</Label>
<Input
variant="secondary"
id="popover-max-height"
defaultValue="none"
className="col-span-2 h-8"
/>
</div>
</div>
</div>
</PopoverContent>
</Popover>
);
}
pnpm dlx shadcn@latest add https://herocn.dev/r/popover.jsonimport { Button } from "@/components/ui/button"
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover"<Popover>
<PopoverTrigger render={<Button variant="outline" />}>
Open Popover
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Title</PopoverTitle>
<PopoverDescription>Description text here.</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>Use the following composition to build a Popover:
Popover
├── PopoverTrigger
└── PopoverContentA simple popover with a header, title, and description.
"use client";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover";
export function PopoverBasic() {
return (
<Popover>
<PopoverTrigger render={<Button variant="tertiary" className="w-fit" />}>
Open Popover
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Dimensions</PopoverTitle>
<PopoverDescription>
Set the dimensions for the layer.
</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
);
}
Use the align prop on PopoverContent to control the horizontal alignment relative to the trigger.
"use client";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
export function PopoverAlign() {
return (
<div className="flex gap-6">
<Popover>
<PopoverTrigger render={<Button variant="tertiary" size="sm" />}>
Start
</PopoverTrigger>
<PopoverContent align="start" className="w-40">
Aligned to start
</PopoverContent>
</Popover>
<Popover>
<PopoverTrigger render={<Button variant="tertiary" size="sm" />}>
Center
</PopoverTrigger>
<PopoverContent align="center" className="w-40">
Aligned to center
</PopoverContent>
</Popover>
<Popover>
<PopoverTrigger render={<Button variant="tertiary" size="sm" />}>
End
</PopoverTrigger>
<PopoverContent align="end" className="w-40">
Aligned to end
</PopoverContent>
</Popover>
</div>
);
}
A popover with form fields inside.
"use client";
import { Button } 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";
export function PopoverForm() {
return (
<Popover>
<PopoverTrigger render={<Button variant="tertiary" />}>
Open Popover
</PopoverTrigger>
<PopoverContent className="w-64">
<PopoverHeader>
<PopoverTitle>Dimensions</PopoverTitle>
<PopoverDescription>
Set the dimensions for the layer.
</PopoverDescription>
</PopoverHeader>
<FieldGroup className="gap-4">
<Field orientation="horizontal">
<FieldLabel htmlFor="popover-form-width" className="w-1/2">
Width
</FieldLabel>
<Input
variant="secondary"
id="popover-form-width"
defaultValue="100%"
/>
</Field>
<Field orientation="horizontal">
<FieldLabel htmlFor="popover-form-height" className="w-1/2">
Height
</FieldLabel>
<Input
variant="secondary"
id="popover-form-height"
defaultValue="25px"
/>
</Field>
</FieldGroup>
</PopoverContent>
</Popover>
);
}
Use the withArrow prop on PopoverContent to show an arrow pointing at the trigger.
"use client";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover";
export function PopoverWithArrow() {
return (
<Popover>
<PopoverTrigger render={<Button variant="tertiary" className="w-fit" />}>
Open Popover
</PopoverTrigger>
<PopoverContent withArrow>
<PopoverHeader>
<PopoverTitle>With Arrow</PopoverTitle>
<PopoverDescription>
This popover has an arrow at its top
</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
);
}
To enable RTL support in shadcn/ui, see the RTL configuration guide.
"use client";
import {
type Translations,
useTranslation,
} from "@/components/language-selector";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover";
const translations: 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;
export function PopoverRtl() {
const { dir, language, t } = useTranslation(translations, "ar");
return (
<div lang={language} dir={dir} className="grid max-w-lg gap-4">
<div className="flex flex-wrap justify-center gap-2">
{physicalSides.map((side) => (
<Popover key={side}>
<PopoverTrigger render={<Button variant="tertiary" size="sm" />}>
{t[side]}
</PopoverTrigger>
<PopoverContent side={side} dir={dir}>
<PopoverHeader>
<PopoverTitle>{t.title}</PopoverTitle>
<PopoverDescription>{t.description}</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
))}
</div>
<div className="flex flex-wrap justify-center gap-2">
{logicalSides.map((side) => (
<Popover key={side}>
<PopoverTrigger render={<Button variant="tertiary" size="sm" />}>
{t[side]}
</PopoverTrigger>
<PopoverContent side={side} dir={dir}>
<PopoverHeader>
<PopoverTitle>{t.title}</PopoverTitle>
<PopoverDescription>{t.description}</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
))}
</div>
</div>
);
}
The popover is built on Base UI Popover. PopoverContent sets default positioning and passes the remaining props to the popup.
| Prop | Type | Default |
|---|---|---|
| align | "start" | "center" | "end" | "center" |
| alignOffset | number | 0 |
| side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
| sideOffset | number | 8 |
| withArrow | boolean | false |
See the Base UI Popover API reference for root, trigger, title, description, and other props.