Popover
Shows details or a short form in a panel anchored to a trigger. The page stays usable behind it.
Installation
pnpm dlx shadcn@latest add @wisty/popover
The CLI installs the Radix or Base UI file to match your project.
If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.
When to use
Use it
- To show more about something on the page without leaving it, like the owner of an account or the details behind a status.
- For a short form next to the thing it changes, like renaming a record in place.
- When the content has links, fields or buttons, so a tooltip won't do, and people should still use the rest of the page while it's open.
Don't use it
- The text is a one-line hint about a control. Use Tooltip.
- A preview of a linked record, opened on hover. Use Hover card.
- People pick one action or option from a list. Use Dropdown menu, or Select for a form value.
- The task is long, has several steps, or must be finished or cancelled before anything else. Use Dialog.
- The content should stay on screen while people work elsewhere. Put it in the page, a Card or a panel.
Usage
Popover
├── PopoverTrigger
└── PopoverContent
└── PopoverHeader
├── PopoverTitle
└── PopoverDescriptionimport { useId } from "react"
import { buttonVariants } from "@/components/ui/button"
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover"
export function OwnerDetails() {
const id = useId()
return (
<Popover>
<PopoverTrigger className={buttonVariants({ variant: "outline" })}>
Account owner
</PopoverTrigger>
<PopoverContent aria-labelledby={`${id}-title`}>
<PopoverHeader>
<PopoverTitle id={`${id}-title`}>Sarah Chen</PopoverTitle>
<PopoverDescription>Sales Director, Austin</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>
)
}There's no close part. To close the popover after Save, control it: pass open and onOpenChange from state to Popover, and call setOpen(false) once the save succeeds.
API reference
| Part | Built on |
|---|---|
Popover | Radix Popover.Root (opens in a new tab) |
PopoverTrigger | Radix Popover.Trigger (opens in a new tab) |
PopoverContent | Radix Popover.Content (opens in a new tab) |
PopoverAnchor | Radix Popover.Anchor (opens in a new tab) |
PopoverHeader | <div> (opens in a new tab) |
PopoverTitle | <h2> (opens in a new tab) |
PopoverDescription | <p> (opens in a new tab) |
PopoverContent
| Prop | Type | Default |
|---|---|---|
align | "center" | "end" | "start" | "center" |
sideOffset | number | 4 |
SLDS mapping
SLDS marks up a popover as a section.slds-popover with a div.slds-popover__body, and puts the header inside the body.
PopoverContentisslds-popoverandslds-popover__bodyin one element.PopoverHeaderisslds-popover__headeras SLDS places it inside the body. It also holds the description.PopoverTitleis the header'sh2.slds-text-heading_small.PopoverDescriptionis body text.PopoverTriggerhas no SLDS class of its own. Style it asslds-button_neutralwithbuttonVariants({ variant: "outline" }).
Known gaps:
- No nubbin. SLDS popovers usually point at their trigger with one (
slds-nubbin_*). - No close button (
slds-popover__close). Escape, an outside click or the trigger closes it. - No footer part (
slds-popover__footer), and no error, warning, walkthrough or feature variants. - SLDS's small body (
slds-popover__body_small, which scrolls past 15rem) isn't exposed.
Keyboard interactions
| Keys | Action |
|---|---|
| Escape | Closes the popover and moves focus back to the trigger. With a Combobox open inside, the first Escape closes only its list. |
| Tab | Past the last control, loops back to the first in Radix projects. In Base UI projects it moves on to whatever follows the trigger, and the popover closes. |
When it opens, focus moves to the first control inside.
Accessibility
- In Radix projects, name the popover: give
PopoverTitleanidand pass it toPopoverContentasaria-labelledby. Nothing else links them, and the title renders as adiv, not a heading. In Base UI projectsPopoverTitlenames the popover already. - Give the trigger a visible label that says what opens, like "Account owner", not "More".
- Clicking outside closes the popover and leaves focus where you clicked, not on the trigger.
Responsive behavior
Rules
- Never nest a Button inside
PopoverTrigger. That's a button in a button; style the trigger withbuttonVariants()instead. - Put the heading and text in
PopoverHeader,PopoverTitleandPopoverDescription, then fields and buttons after the header. - Don't add borders, shadows, padding or colors to
PopoverContent. They come from the theme. - Don't add a close button unless the content needs one. Escape and an outside click already close it.
- If you add open or close animation through
className, put it behindmotion-safe:.
Related
- TooltipShows a short hint about a control while it has hover or keyboard focus.
- Hover cardPreviews a linked record or person while the pointer rests on the link.
- DialogOpens a window over the page for a short task or a message that needs an answer.
- Dropdown menuOpens a menu of actions or options from a button, like a record's row actions.