Build · New
Popover
A small surface for one quick task, opened next to the control that asked for it.
Preview
Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.
Installation
SoonInstalling from the registry opens at launch. Until then the command below is what it will be.
npx shadcn@latest add @ritla/popoverDependencies it brings: @base-ui/react, @phosphor-icons/react
Usage
import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";<Popover>
<PopoverTrigger render={<Button variant="outline" />}>Share</PopoverTrigger>
<PopoverContent title="Share this report" description="Anyone with the link can view it.">
…
</PopoverContent>
</Popover>Examples
Filter
A few choices and Apply (PopoverClose), which closes it and returns focus to the trigger. No close button here: Cancel and Esc are enough.
Sides
side: Bottom (default), Top, Start and End. Start and End follow the reading direction, so Start is on the right in Arabic, and the arrow always points at the trigger. Without room, it flips to the other side.
API reference
Popover
| Prop | Type | Default | Description |
|---|---|---|---|
open / defaultOpen | boolean | To control it from code. | |
onOpenChange | (open, details) => void | When it opens or closes. |
PopoverTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
render | ReactElement | The visible control: <Button … />. |
PopoverContent
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | Figma's Title: what it's for; names the dialog. | |
description | ReactNode | Figma's Description: one line under the title. | |
children | ReactNode | The body: a field and a button, or a few choices. | |
side | "bottom" | "top" | "start" | "end" | "bottom" | Figma's Side. Flips when there isn't room. |
align | "start" | "center" | "end" | "center" | Where it lines up along that side. |
arrow | boolean | true | The arrow, pointing at the trigger. |
closeButton | boolean | true | The close button at the header's inline end. |
closeLabel | string | "Close" / «إغلاق» | The close button's name. |
sideOffset | number | 12 | Space between trigger and surface. |
initialFocus / finalFocus | RefObject<HTMLElement> | Where focus starts and where it returns. |
PopoverClose
| Prop | Type | Default | Description |
|---|---|---|---|
render | ReactElement | A control in the body that closes it: Apply, Done. |
Accessibility
Keyboard
Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.
| Key | Action |
|---|---|
| EnterSpace | On the trigger: opens it and moves focus to its first field, else its first control. |
| TabShift+Tab | Between its controls. Focus isn't trapped: tabbing out closes it. |
| Esc | Closes it and returns focus to the trigger. |
ARIA
- A non-modal dialog (
role="dialog") named by its title and described by its description. - The trigger has
aria-expandedandaria-controls. - A click outside closes it; typing inside never does.
- No popover inside another: use a Dialog for bigger tasks.
Arabic and RTL notes
Mirrors
- Start and End: Start opens on the right in Arabic.
- The close button sits at the header's inline end: on the left.
- The arrow follows the side and points at the trigger in both directions.
Does not mirror
- URLs, codes and IDs stay left to right.
- Bottom and Top don't change.
- Numerals
- Numbers inside follow the numerals setting.
- Arabic typography
- Title
label-16Semibold, descriptionlabel-14. - Mixed-direction text
- It carries the trigger's direction and language into the portal, so an Arabic preview on an English page stays Arabic. A field with
dir="ltr"keeps the whole link left to right.