ComponentsPopover
Popover
Show extra content next to a button or other trigger.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry files- React Aria
(react-aria)Accessibility hooks and overlay utilities used by the implementation.
- React Aria Components
(react-aria-components)Accessible interaction primitives, including keyboard behavior, focus handling, and ARIA semantics.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import {
Popover,
PopoverClose,
PopoverContent,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@dethink/components";Try the examples, then open the code to use them in your app.
Basic
Trigger, titled content, and a footer close button.
Show sourceexamples/popover/basic.tsx
"use client";
import { Users, Share2 } from "lucide-react";
import {
Popover,
PopoverClose,
PopoverContent,
PopoverDescription,
PopoverFooter,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@dethink/components";
export function PopoverBasic() {
return (
<div className="flex justify-center">
<Popover>
<PopoverTrigger variant="outline">
<Share2 aria-hidden="true" className="size-4" />
Share dashboard
</PopoverTrigger>
<PopoverContent showArrow>
<PopoverHeader>
<PopoverTitle>Share dashboard</PopoverTitle>
<PopoverDescription>
Keep your team on the same page.
</PopoverDescription>
</PopoverHeader>
<div className="border-border bg-muted/40 flex items-center gap-3 rounded-lg border p-3">
<span className="border-border bg-background text-muted-foreground flex size-9 shrink-0 items-center justify-center rounded-md border">
<Users aria-hidden="true" className="size-4" />
</span>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">Workspace members</p>
<p className="text-muted-foreground text-xs">
Everyone in your workspace
</p>
</div>
<span className="text-muted-foreground shrink-0 text-xs">
Can view
</span>
</div>
<PopoverFooter>
<PopoverClose size="sm" variant="solid">
Done
</PopoverClose>
</PopoverFooter>
</PopoverContent>
</Popover>
</div>
);
}Placement and arrow
placement picks the side; showArrow points the panel at its trigger. Sides flip automatically when space runs out.
Show sourceexamples/popover/placement.tsx
"use client";
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@dethink/components";
const placements = ["top", "right", "bottom", "left"] as const;
export function PopoverPlacement() {
return (
<div className="flex flex-wrap justify-center gap-3">
{placements.map((placement) => (
<Popover key={placement}>
<PopoverTrigger variant="soft" size="sm">
{placement}
</PopoverTrigger>
<PopoverContent placement={placement} showArrow>
<PopoverHeader>
<PopoverTitle className="capitalize">
{placement} placement
</PopoverTitle>
<PopoverDescription>
The arrow follows the trigger. This panel flips sides when it
needs more room.
</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
))}
</div>
);
}Examples that combine components for common tasks.
Inline edit
The value on the page is the trigger. The controlled popover edits a draft, so Cancel discards and Save commits — the displayed value never flickers mid-edit.
Monthly spend limit
Alerts fire at 80% of the limit.
Show sourceexamples/popover/recipe-inline-edit.tsx
"use client";
import { useId, useState } from "react";
import { Pencil } from "lucide-react";
import {
Button,
Field,
FieldControl,
FieldLabel,
FieldError,
Input,
Popover,
PopoverClose,
PopoverContent,
PopoverDescription,
PopoverFooter,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@dethink/components";
/**
* Inline edit: the value on the page is the trigger. A controlled popover
* carries a draft, so Cancel discards and Save commits — the page value
* never flickers mid-edit.
*/
export function PopoverRecipeInlineEdit() {
const [limit, setLimit] = useState(500);
const [draft, setDraft] = useState(String(limit));
const [open, setOpen] = useState(false);
const [submitted, setSubmitted] = useState(false);
const fieldId = useId();
const amount = Number(draft);
const valid = draft.trim() !== "" && Number.isFinite(amount) && amount >= 0;
return (
<div className="border-border bg-background mx-auto flex w-full max-w-sm items-center justify-between gap-4 rounded-xl border p-4 shadow-sm">
<div className="min-w-0">
<p className="text-sm font-medium">Monthly spend limit</p>
<p className="text-muted-foreground text-sm">
Alerts fire at 80% of the limit.
</p>
</div>
<Popover
open={open}
onOpenChange={(next) => {
setOpen(next);
if (next) {
setDraft(String(limit));
setSubmitted(false);
}
}}
>
<PopoverTrigger
variant="outline"
className="shrink-0 tabular-nums"
aria-label={`Edit monthly spend limit, currently $${limit}`}
>
${limit}
<Pencil
aria-hidden="true"
className="text-muted-foreground size-3.5"
/>
</PopoverTrigger>
<PopoverContent placement="bottom end" showArrow>
<form
className="grid gap-4"
noValidate
onSubmit={(event) => {
event.preventDefault();
setSubmitted(true);
if (!valid) return;
setLimit(amount);
setOpen(false);
}}
>
<PopoverHeader>
<PopoverTitle>Edit spend limit</PopoverTitle>
<PopoverDescription>
Set a monthly budget for your workspace.
</PopoverDescription>
</PopoverHeader>
<Field id={fieldId} invalid={submitted && !valid}>
<FieldLabel>Limit (USD)</FieldLabel>
<FieldControl asChild>
<Input
type="number"
min={0}
step="any"
required
value={draft}
onChange={(event) => setDraft(event.target.value)}
/>
</FieldControl>
{submitted && !valid ? (
<FieldError>Enter an amount of 0 or more.</FieldError>
) : null}
</Field>
<PopoverFooter>
<PopoverClose size="sm" variant="ghost">
Cancel
</PopoverClose>
<Button type="submit" size="sm">
Save changes
</Button>
</PopoverFooter>
</form>
</PopoverContent>
</Popover>
</div>
);
}Popover coordinates trigger and content; content carries placement and arrow options.
| Prop | What it does | Default |
|---|---|---|
open / defaultOpen / onOpenChangeboolean / boolean / (open) => void | Controlled or uncontrolled visibility. | Not set |
PopoverTrigger / PopoverClosevariant + size (Button API) | Anchor button and close buttons; PopoverClose also accepts onPress for save-on-close patterns. | Not set |
PopoverContent — placement"top" | "bottom" | "left" | "right" (+ " start"/" end") | Preferred side and alignment relative to the trigger. | "bottom" |
PopoverContent — showArrowboolean | Renders a pointing arrow tied to the trigger. | false |
PopoverContent — offsetnumber | Distance in pixels between trigger and panel. | 8 |
PopoverHeader / PopoverTitle / PopoverDescription / PopoverFootersection components | Anatomy pieces; title and description label the popover dialog. | Not set |