Popover
Radix-powered floating panel anchored to a trigger. w-72 rounded-md bg-popover shadow-md p-4. Used for supplementary content (settings snippets, help, notifications) that doesn't warrant a Dialog. Also the canonical surface for feature Tutorial popovers.
Anatomy
- 1Popover (root)
Radix Root that manages open/close state. Uncontrolled by default.
- 2PopoverTrigger
Any focusable element. Pass asChild so the Popover wraps YOUR element instead of adding its own.
- 3PopoverAnchor
Optional. Anchors the content to a different element than the trigger (advanced positioning).
- 4PopoverContent
The floating panel: w-72 rounded-md border bg-popover shadow-md p-4, portalled to body, animated in via animate-dialog-in.
States
Open / Closed. Focus is trapped inside the panel while open; clicking outside or pressing Esc closes it. Portal escapes any overflow-hidden ancestor.
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open</Button>
</PopoverTrigger>
<PopoverContent>Content</PopoverContent>
</Popover>Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
The canonical Yuno pattern for onboarding new features and sections. Title + description + Skip (ghost) / step counter (muted center) / Got it! (primary right). Reach for this recipe every time you ship a new feature that needs a hint.
function TutorialPopover() {
const [step, setStep] = React.useState(1);
const total = 3;
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Show tutorial</Button>
</PopoverTrigger>
<PopoverContent align="start" className="w-80">
<div className="grid gap-2">
<div className="text-base font-medium text-foreground">Tutorial title</div>
<p className="text-sm text-muted-foreground">
Example text: Now you can personalize your experience…
</p>
</div>
<div className="mt-4 flex items-center justify-between gap-3">
<Button variant="outline" size="sm">Skip</Button>
<span className="text-sm text-muted-foreground">Step {step} of {total}</span>
<Button size="sm" onClick={() => setStep((s) => s + 1)}>
{step < total ? "Next" : "Got it!"}
</Button>
</div>
</PopoverContent>
</Popover>
);
}One-shot tip that highlights a new UI area. Title + description + single primary 'Got it!' aligned right. Shorter than a tutorial and dismissible in one click.
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Show tip</Button>
</PopoverTrigger>
<PopoverContent align="start" className="w-80">
<div className="grid gap-2">
<div className="text-base font-medium text-foreground">Customize your table!</div>
<p className="text-sm text-muted-foreground">
Now you can personalize your experience according to your preferences.
You can customize, rearrange, hide/show, and resize the columns.
</p>
</div>
<div className="mt-4 flex justify-end">
<Button size="sm">Got it!</Button>
</div>
</PopoverContent>
</Popover>Small form fields inside a popover, launched from a control button. Use for quick tweaks that don't warrant leaving the current context.
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Dimensions</Button>
</PopoverTrigger>
<PopoverContent align="start" className="w-80">
<div className="grid gap-3">
<div className="grid gap-1">
<div className="text-base font-medium text-foreground">Dimensions</div>
<p className="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
</div>
<div className="grid grid-cols-3 items-center gap-3">
<Label htmlFor="width" className="text-right">Width</Label>
<Input id="width" defaultValue="100%" className="col-span-2 h-8" />
</div>
<div className="grid grid-cols-3 items-center gap-3">
<Label htmlFor="maxWidth" className="text-right">Max. width</Label>
<Input id="maxWidth" defaultValue="300px" className="col-span-2 h-8" />
</div>
</div>
</PopoverContent>
</Popover>Bell IconButton opens a wider popover with a scrollable list of notifications. Common in the top bar utilities cluster.
<Popover>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon" aria-label="Notifications">
<BellSimple weight="light" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-80 p-0">
<div className="border-b px-4 py-3 text-sm font-medium">Notifications</div>
<ul className="grid gap-0.5 p-2">
{items.map(n => (
<li key={n.id} className="rounded-sm p-3 text-sm hover:bg-accent">
<div className="text-foreground">{n.title}</div>
<div className="text-xs text-muted-foreground">{n.time}</div>
</li>
))}
</ul>
</PopoverContent>
</Popover>Import
Copy this import line at the top of the file where you compose this atom.
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| Popover.open / defaultOpen | boolean | — | Controlled or uncontrolled open state. Controlled needs onOpenChange. |
| Popover.onOpenChange | (open: boolean) => void | — | Fires whenever the panel opens or closes. |
| Popover.modal | boolean | false | When true, blocks interaction with outside elements — behaves closer to a Dialog. |
| PopoverTrigger.asChild | boolean | false | Merges props onto the child element (Radix Slot). Always pass when wrapping a real element (Button, IconButton). |
| PopoverContent.side | "top" | "right" | "bottom" | "left" | "bottom" | Preferred side. Radix auto-flips if there's no room. |
| PopoverContent.align | "start" | "center" | "end" | "center" | Alignment along the side. Tutorials usually use align='start'; notifications trays use align='end'. |
| PopoverContent.sideOffset | number | 4 | Distance in px between trigger and content. |
| PopoverContent.className | string | — | Merged. Default is w-72 p-4. Override w-* for wider panels (notifications, tutorials use w-80). Use p-0 when you handle padding per-section. |
| PopoverAnchor | component | — | Optional anchor different from the trigger. Advanced — most Popovers don't need it. |
When to use
- Small forms or previews launched from a control.
- Notifications tray triggered by the bell IconButton.
- Feature tutorials and onboarding tips over new sections.
- Non-critical supplementary content anchored to a trigger.
When not to use
- For focused editing that needs the full page context — use Sheet or Dialog.
- For informational tooltips — use Tooltip.
- For critical decisions the user must not miss — use Dialog.
Usage
- Keep the content under 400px tall.
- Anchor it to a real trigger — never floating without one.
- For tutorials, always include Skip so users can dismiss the whole flow.
- For onboarding tips, provide a single primary Got it! so the action is unambiguous.
- Don't stack Popovers.
- Don't rely on a Popover for critical decisions — use Dialog.
- Don't hide destructive actions inside a Popover — they need a Dialog confirmation.
- Don't skip the animation — animate-dialog-in is the Yuno default.
Related
Cross-links to atoms and patterns you may reach for next.
- DialogFor blocking decisions or critical modals that need the user's full attention.
- SheetFor focused editing that needs more room than a Popover.
- TooltipFor hover hints without interactive content.
- Hover cardFor preview-on-hover with richer content than a Tooltip.
- Dropdown menuWhen the panel should offer a list of actions instead of free-form content.