labsUI

Popover

Non-modal panel anchored to a Button for filters, pickers and quick edits. Focus moves in, Esc returns it, outside clicks close; Selects and menus opened inside don't count as outside.

npx @kznlabs/labsui add popover
import { Button } from "@/components/ui/button";import {  Popover,  PopoverContent,  PopoverTrigger,} from "@/components/ui/popover";export function ClipFilters() {  return (    <Popover>      <PopoverTrigger variant="outline">Filters</PopoverTrigger>      <PopoverContent align="start" className="grid gap-3">        <p className="font-medium text-sm">Filter clips</p>        <Button size="sm">Apply</Button>      </PopoverContent>    </Popover>  );}

<Popover>

Non-modal panel anchored to a button: filters, pickers, quick edits. Controlled or uncontrolled.

  • children*
    ReactNode

    PopoverTrigger and PopoverContent.

  • defaultOpendefault false
    boolean

    Open at first render (uncontrolled).

  • onOpenChange
    (open: boolean) => void

    Called on the trigger, Esc or an outside click.

  • open
    boolean

    Controlled state; pair with onOpenChange.

PropTypeDefault
children*

PopoverTrigger and PopoverContent.

ReactNode—
defaultOpen

Open at first render (uncontrolled).

booleanfalse
onOpenChange

Called on the trigger, Esc or an outside click.

(open: boolean) => void—
open

Controlled state; pair with onOpenChange.

boolean—

<PopoverTrigger>

  • children
    ReactNode

    Label; its width is reserved so loading and success never resize the button.

  • confirmLabel
    ReactNode

    Two-step action: first click swaps the label to this, second click runs onClick.

  • confirmTimeout
    number

    Ms before an armed confirm step resets. Also resets on blur or Esc.

  • loading
    boolean

    Disables the button and shows a spinner before the label.

  • loadingLabel
    ReactNode

    Label shown while loading, with the spinner. Defaults to children.

  • size
    "default" | "sm" | "lg" | "icon"

    Height and padding; "icon" is square, so give it an aria-label.

  • status
    ButtonStatus

    In-place feedback: loading spins, success swaps the label to successLabel.

  • successLabel
    ReactNode

    Text shown on status="success", with a check icon. Defaults to "Done".

  • swap
    SwapDirection

    Where the old label slides out to on a swap. Default "left".

  • variant
    "default" | "secondary" | "outline" | "ghost" | "destructive"

    Fill and emphasis: one primary action per view, destructive for irreversible ones.

PropTypeDefault
children

Label; its width is reserved so loading and success never resize the button.

ReactNode—
confirmLabel

Two-step action: first click swaps the label to this, second click runs onClick.

ReactNode—
confirmTimeout

Ms before an armed confirm step resets. Also resets on blur or Esc.

number—
loading

Disables the button and shows a spinner before the label.

boolean—
loadingLabel

Label shown while loading, with the spinner. Defaults to children.

ReactNode—
size

Height and padding; "icon" is square, so give it an aria-label.

"default" | "sm" | "lg" | "icon"—
status

In-place feedback: loading spins, success swaps the label to successLabel.

ButtonStatus—
successLabel

Text shown on status="success", with a check icon. Defaults to "Done".

ReactNode—
swap

Where the old label slides out to on a swap. Default "left".

SwapDirection—
variant

Fill and emphasis: one primary action per view, destructive for irreversible ones.

"default" | "secondary" | "outline" | "ghost" | "destructive"—

Also accepts every Button prop.

<PopoverContent>

Focus moves in on open. Esc or an outside click closes it; Esc returns focus to the trigger. Not a focus trap: Tab past the end closes it and moves on, like any non-modal popup.

  • children*
    ReactNode

    Panel content.

  • aligndefault "center"
    "start" | "center" | "end"

    Alignment with the trigger.

  • sidedefault "bottom"
    "top" | "bottom"

    Preferred side; flips when there is no room.

PropTypeDefault
children*

Panel content.

ReactNode—
align

Alignment with the trigger.

"start" | "center" | "end""center"
side

Preferred side; flips when there is no room.

"top" | "bottom""bottom"

* required. Every component also takes className.