Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Components

Free

Popover for Ark UI

Open a short note or a few fields beside the trigger, while the page stays usable. For a small edit in React with Ark UI, when the panel should not block the page.

Framework
UI library

The panel sits next to the button that opened it. You can still use the page around it.

Use it for a small edit or a short explanation. A task that should block the page until it is finished is a Dialog. In React, Ark UI moves focus into the panel and returns it on Escape.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Popover anatomy: trigger, title, positioner, arrow, description, content
PopoverArrow
└── PopoverArrowTip
PopoverAnchor
PopoverTrigger
PopoverIndicator
PopoverPositioner
PopoverContent
PopoverTitle
PopoverDescription
PopoverClose

Usage

import { Button } from "@/components/ui/button";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
  Popover, PopoverContent, PopoverDescription, PopoverHeader,
  PopoverTitle, PopoverTrigger,
} from "@/components/ui/popover";
<Popover>
  <PopoverTrigger asChild>
    <Button variant="outline">Open popover</Button>
  </PopoverTrigger>
  <PopoverContent className="w-64 max-w-full">
    <PopoverHeader>
      <PopoverTitle>Dimensions</PopoverTitle>
      <PopoverDescription>
        Set the dimensions for the layer.
      </PopoverDescription>
    </PopoverHeader>
    <FieldGroup className="min-w-0 gap-3">
      <Field className="min-w-0" orientation="horizontal">
        <FieldLabel className="w-1/2">Width</FieldLabel>
        <Input className="min-w-0" defaultValue="100%" />
      </Field>
      <Field className="min-w-0" orientation="horizontal">
        <FieldLabel className="w-1/2">Height</FieldLabel>
        <Input className="min-w-0" defaultValue="25px" />
      </Field>
    </FieldGroup>
  </PopoverContent>
</Popover>

Accessibility

Space
Opens/closes the popover.
Enter
Opens/closes the popover.
Tab
Moves focus to the next focusable element within the content. Note: If there are no focusable elements, focus is moved to the next focusable element after the trigger.
Shift+Tab
Moves focus to the previous focusable element within the content. Note: If there are no focusable elements, focus is moved to the trigger.
Escape
Closes the popover and moves focus to the trigger.

Demos

Basic

A title and one sentence, with no inputs. The panel lines up with the start of the button rather than its center, so it reads as a note attached to that control. Use this for a definition that should stay open while someone reads it. A Tooltip is the lighter choice when a single line on hover or focus is enough and does not need a dismiss.

Preview width

Alignments

Three triggers place the panel at the start, the center, or the end of the button. Prefer start or end near a corner, and keep one alignment on a toolbar so the panels do not jump. Ark-based stacks use positioning.placement on Popover, Base UI and Radix use align on PopoverContent, and React Aria uses placement with a space, as in bottom start.

Preview width

Form

Width and height live in the panel, under a title that says what you are editing. Focus moves into the fields when it opens, and Escape or a click outside closes it. Use this for a small edit that should stay next to its button. More than a couple of fields, or a page that must wait, belongs in a Dialog.

Preview width

Rtl

The row sets dir to rtl, and each button opens the panel toward a physical side: left, top, bottom, or right. Those edges do not mirror just because the language does. The trigger text is Arabic so it runs with the page. If the panel should swap sides with the writing direction, use a logical placement instead of left or right.

Preview width

FAQ

How do I open the Popover from code in React?

Pass open and onOpenChange on Popover; the callback gets { open }.

It still needs its trigger, or an anchor, to know where to sit.

How do I align the Ark UI Popover to the start or end of its trigger?

Set a placement in positioning on Popover: bottom-start, bottom (the default) or bottom-end, and the other sides the same way.

The Alignments demo above shows all three.

How do I position the Popover next to another element?

Wrap that element in PopoverAnchor: the trigger still opens the Popover, and the content positions against the anchor.

How do I make the Popover as wide as its trigger?

Give PopoverContent a width from the CSS variable the positioner sets: w-(--reference-width). It updates as the trigger resizes, which suits pickers and menus under a field.

How do I put a form inside a Popover?

Put the fields in PopoverContent, as in the Form demo: focus moves into the panel when it opens, and Escape or a click outside closes it. To close it after saving, control its open state and set it to closed in your submit handler.

When should I use a Popover instead of a Dialog, a Tooltip or a Hover Card?

Use a Popover for a short form or extra controls next to what opened it, while the rest of the page stays usable. A Dialog blocks the page for a task that needs full attention, a Tooltip gives a one-line hint on hover or focus, and a Hover Card previews content while the pointer rests on a link.

Is the Ark UI Popover accessible?

Yes. The trigger is a button that tells assistive tech whether the panel is open, focus moves into the content and back to the trigger when it closes, and Escape closes it. Add a PopoverTitle so the panel has a name screen readers can announce.

How do I install the Popover in React?

Run the command from the Installation section above: it adds the Popover and the parts it uses to your project's ui folder. The shadcn CLI writes components/ui/popover.tsx, a file you own and can edit.

API Reference

Popover

data-slot="popover"

PropType
open?boolean

The controlled open state of the popover.

defaultOpen?boolean

The initial open state of the popover when rendered. Use when you don't need to control the open state of the popover.

onOpenChange?(details: OpenChangeDetails) → void

Function invoked when the popover opens or closes.

Type
(details: OpenChangeDetails) → void
Parameters

details – { open: boolean }

Returns
void
triggerValue?string | null

The controlled trigger value.

Type
string | null
defaultTriggerValue?string | null

The initial trigger value when rendered. Use when you don't need to control the trigger value.

Type
string | null
onTriggerValueChange?(details: TriggerValueChangeDetails) → void

Function called when the trigger value changes.

Type
(details: TriggerValueChangeDetails) → void
Parameters

details – { value: string | null; triggerElement: HTMLElement | null }

Returns
void
autoFocus?boolean

Whether to automatically set focus on the first focusable content within the popover when opened.

Default
true
closeOnEscape?boolean

Whether to close the popover when the escape key is pressed.

Default
true
closeOnInteractOutside?boolean

Whether to close the popover when the user clicks outside of the popover.

Default
true
dir?"ltr" | "rtl"

The document's text/writing direction.

Type
"ltr" | "rtl"
Default
"ltr"
finalFocusEl?() → MaybeElement

Element to receive focus when the popover is closed.

Returns
MaybeElement
hideMode?"display-none" | "activity"

How to hide content when mounted but not present. - display-none: HTML hidden attribute. Effects stay alive. - activity: React 19 <Activity mode="hidden">. Effects pause. Requires React 19+.

Type
"display-none" | "activity"
Default
"display-none"
id?string

The unique identifier of the machine.

ids?object

The ids of the elements in the popover. Useful for composition.

Type
{ anchor?: string; trigger?: string | ((value?: string) → string); content?: string; title?: string; description?: string; closeTrigger?: string; positioner?: string; arrow?: string }
initialFocusEl?() → HTMLElement | null | false

The element to focus on when the popover is opened. Return false to skip moving focus, or null to use the default.

Type
() → HTMLElement | null | false
Returns
HTMLElement | null | false
lazyMount?boolean

Whether to enable lazy mounting. Ark default: false.

Default
true
modal?boolean

Whether the popover should be modal. When set to true: - interaction with outside elements will be disabled - only popover content will be visible to screen readers - scrolling is blocked - focus is trapped within the popover Ark default: false.

Default
true
onEscapeKeyDown?(event: KeyboardEvent) → void

Function called when the escape key is pressed.

Parameters

event – { altKey: boolean; charCode: number; ctrlKey: boolean; code: string; getModifierState: (key: string) → boolean; key: string; keyCode: number; locale: string; location: number; metaKey: boolean; repeat: boolean; shiftKey: boolean; which: number }

Returns
void
onFocusOutside?(event: FocusOutsideEvent) → void

Function called when the focus is moved outside the component.

Type
(event: FocusOutsideEvent) → void
Parameters

event – FocusOutsideEvent

Returns
void
onInteractOutside?(event: InteractOutsideEvent) → void

Function called when an interaction happens outside the component.

Type
(event: InteractOutsideEvent) → void
Parameters

event – InteractOutsideEvent

Returns
void
onPointerDownOutside?(event: PointerDownOutsideEvent) → void

Function called when the pointer is pressed down outside the component.

Type
(event: PointerDownOutsideEvent) → void
Parameters

event – PointerDownOutsideEvent

Returns
void
onRequestDismiss?(event: LayerDismissEvent) → void

Function called when this layer is closed due to a parent layer being closed.

Type
(event: LayerDismissEvent) → void
Parameters

event – LayerDismissEvent

Returns
void
persistentElements?Array<() → Element | null>

Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event

Type
Array<() → Element | null>
portalled?boolean

Whether the popover is portalled. This will proxy the tabbing behavior regardless of the DOM position of the popover content.

Default
true
positioning?object

The user provided options used to position the popover content.

Type
{ restoreStyles?: boolean; applyStyles?: boolean; hideWhenDetached?: boolean; strategy?: "absolute" | "fixed"; placement?: Placement; offset?: { mainAxis?: number; crossAxis?: number; }; gutter?: number; shift?: number; overflowPadding?: number; arrowPadding?: number; flip?: boolean | Placement[]; slide?: boolean; overlap?: boolean; sameWidth?: boolean; fitViewport?: boolean; sizeMiddleware?: boolean; boundary?: (() → Boundary) | Boundary | "clipping-ancestors"; listeners?: boolean | AutoUpdateOptions; onComplete?: (data: ComputePositionReturn) → void; onPositioned?: ((data: { placed: boolean; }) → void); getAnchorElement?: () → HTMLElement | VirtualElement | null; getAnchorRect?: (element: HTMLElement | VirtualElement | null) → AnchorRect | null; updatePosition?: ((data: { updatePosition: () → Promise<void>; floatingElement: HTMLElement | null; }) → void | Promise<void>) }
restoreFocus?boolean

Whether to restore focus to the element that had focus before the popover was opened.

Default
true
skipAnimationOnMount?boolean

Whether to allow the initial presence animation.

Default
false
translations?object

Specifies the localized strings that identifies the accessibility elements and their states.

Type
{ closeTriggerLabel?: string }
unmountOnExit?boolean

Whether to unmount on exit. Ark default: false.

Default
true
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

PopoverArrow

<div> data-slot="popover-arrow"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

CSS variableType
--arrow-backgroundstring

Use this variable to style the arrow background.

--arrow-offsetstring

The offset position of the arrow.

--arrow-sizestring

The size of the arrow.

--arrow-size-halfstring

Half the size of the arrow.

PopoverArrowTip

<div>

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

PopoverAnchor

<div> data-slot="popover-anchor"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

PopoverTrigger

<button> data-slot="popover-trigger"

PropType
value?string

The value that identifies this specific trigger.

asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

AttributeType
data-current?string

Present when current.

data-placement?string

The placement of the trigger.

data-side?string

The side of the trigger that the trigger is positioned on.

data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"
data-value?string

The value of the item.

PopoverPositioner

<div> data-slot="popover-positioner"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

AttributeType
data-has-nested?string

Present when the positioner has nested popovers.

data-nested?string

Present when the positioner is nested within another popover.

CSS variableType
--available-heightstring

The available height in viewport.

--available-widthstring

The available width in viewport.

--reference-heightstring

The height of the root.

--reference-widthstring

The width of the reference element.

--transform-originstring

The transform origin for animations.

--xstring

The x position for transform.

--ystring

The y position for transform.

--z-indexstring

The z-index value.

PopoverContent

<div> data-slot="popover-content"

PropType
showCloseButton?boolean

Show close button at the top right corner. Ark default: true.

Default
false
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

AttributeType
data-expanded?string

Present when expanded.

data-has-nested?string

Present when another popover is nested inside it.

data-nested?string

Present when it's nested inside another popover.

data-placementstring

The placement of the content.

data-side"top" | "right" | "bottom" | "left"

The side of the trigger that the content is positioned on.

Type
"top" | "right" | "bottom" | "left"
data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"
CSS variableType
--layer-indexstring

The index of the dismissable in the layer stack.

--nested-layer-countstring

The number of nested popovers.

--transform-originstring

Where the content scales from, on its trigger's side.

PopoverTitle

<h2> data-slot="popover-title"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

PopoverDescription

<div> data-slot="popover-description"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

PopoverClose

<button> data-slot="popover-close-trigger"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

PopoverHeader

<div> data-slot="popover-header"

PropType
description?string

The description of the popover header.

title?string

The title of the popover header.

asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

PopoverBody

<div> data-slot="popover-body"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

CSS variableType
--spacestring

PopoverFooter

<div> data-slot="popover-footer"

PropType
asChild?boolean

Use the provided child element as the default rendered element, combining their props and behavior.

className?string

Merged with the part's classes.

CSS variableType
--spacestring

usePopover

PropType
open?boolean

The controlled open state of the popover.

defaultOpen?boolean

The initial open state of the popover when rendered. Use when you don't need to control the open state of the popover.

onOpenChange?(details: OpenChangeDetails) → void

Function invoked when the popover opens or closes.

Type
(details: OpenChangeDetails) → void
Parameters

details – { open: boolean }

Returns
void
triggerValue?string | null

The controlled trigger value.

Type
string | null
defaultTriggerValue?string | null

The initial trigger value when rendered. Use when you don't need to control the trigger value.

Type
string | null
onTriggerValueChange?(details: TriggerValueChangeDetails) → void

Function called when the trigger value changes.

Type
(details: TriggerValueChangeDetails) → void
Parameters

details – { value: string | null; triggerElement: HTMLElement | null }

Returns
void
autoFocus?boolean

Whether to automatically set focus on the first focusable content within the popover when opened.

Default
true
closeOnEscape?boolean

Whether to close the popover when the escape key is pressed.

Default
true
closeOnInteractOutside?boolean

Whether to close the popover when the user clicks outside of the popover.

Default
true
finalFocusEl?() → MaybeElement

Element to receive focus when the popover is closed.

Returns
MaybeElement
id?string

The unique identifier of the machine.

ids?object

The ids of the elements in the popover. Useful for composition.

Type
{ anchor?: string; trigger?: string | ((value?: string) → string); content?: string; title?: string; description?: string; closeTrigger?: string; positioner?: string; arrow?: string }
initialFocusEl?() → HTMLElement | null | false

The element to focus on when the popover is opened. Return false to skip moving focus, or null to use the default.

Type
() → HTMLElement | null | false
Returns
HTMLElement | null | false
modal?boolean

Whether the popover should be modal. When set to true: - interaction with outside elements will be disabled - only popover content will be visible to screen readers - scrolling is blocked - focus is trapped within the popover

Default
false
onEscapeKeyDown?(event: KeyboardEvent) → void

Function called when the escape key is pressed.

Parameters

event – { altKey: boolean; charCode: number; ctrlKey: boolean; code: string; getModifierState: (key: string) → boolean; key: string; keyCode: number; locale: string; location: number; metaKey: boolean; repeat: boolean; shiftKey: boolean; which: number }

Returns
void
onFocusOutside?(event: FocusOutsideEvent) → void

Function called when the focus is moved outside the component.

Type
(event: FocusOutsideEvent) → void
Parameters

event – FocusOutsideEvent

Returns
void
onInteractOutside?(event: InteractOutsideEvent) → void

Function called when an interaction happens outside the component.

Type
(event: InteractOutsideEvent) → void
Parameters

event – InteractOutsideEvent

Returns
void
onPointerDownOutside?(event: PointerDownOutsideEvent) → void

Function called when the pointer is pressed down outside the component.

Type
(event: PointerDownOutsideEvent) → void
Parameters

event – PointerDownOutsideEvent

Returns
void
onRequestDismiss?(event: LayerDismissEvent) → void

Function called when this layer is closed due to a parent layer being closed.

Type
(event: LayerDismissEvent) → void
Parameters

event – LayerDismissEvent

Returns
void
persistentElements?Array<() → Element | null>

Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event

Type
Array<() → Element | null>
portalled?boolean

Whether the popover is portalled. This will proxy the tabbing behavior regardless of the DOM position of the popover content.

Default
true
positioning?object

The user provided options used to position the popover content.

Type
{ restoreStyles?: boolean; applyStyles?: boolean; hideWhenDetached?: boolean; strategy?: "absolute" | "fixed"; placement?: Placement; offset?: { mainAxis?: number; crossAxis?: number; }; gutter?: number; shift?: number; overflowPadding?: number; arrowPadding?: number; flip?: boolean | Placement[]; slide?: boolean; overlap?: boolean; sameWidth?: boolean; fitViewport?: boolean; sizeMiddleware?: boolean; boundary?: (() → Boundary) | Boundary | "clipping-ancestors"; listeners?: boolean | AutoUpdateOptions; onComplete?: (data: ComputePositionReturn) → void; onPositioned?: ((data: { placed: boolean; }) → void); getAnchorElement?: () → HTMLElement | VirtualElement | null; getAnchorRect?: (element: HTMLElement | VirtualElement | null) → AnchorRect | null; updatePosition?: ((data: { updatePosition: () → Promise<void>; floatingElement: HTMLElement | null; }) → void | Promise<void>) }
restoreFocus?boolean

Whether to restore focus to the element that had focus before the popover was opened.

Default
true
translations?object

Specifies the localized strings that identifies the accessibility elements and their states.

Type
{ closeTriggerLabel?: string }

With other components

  • Button

    Buttons

    Trigger for the panel

  • Field

    Inputs

    Label stacked with input

  • Input

    Inputs

    Width, height, filters

  • Pick a date

  • Command

    Overlays

    Searchable list

  • Separator

    Layout

    Split header from fields