XDSPopover@xds/core · Popover
Preview coming soon
Usage
A click-triggered overlay anchored to a button or trigger element. Use it for secondary actions, inline confirmations, or supplementary information that does not warrant a full dialog. For hover previews use HoverCard, for brief helper text use Tooltip.Best practices
| Guidance | Practices |
|---|---|
| Do | Keep popover content focused on a single task or piece of information. |
| Do | Provide a clear way to close — either by clicking outside or with an explicit close button. |
| Don't | Nest popovers inside other popovers — it creates confusing focus and navigation. |
| Don't | Use a popover for content that requires heavy user input — use a Dialog instead. |
| Don't | Put too much content in a popover — if it needs scrolling, use a Dialog instead. |
Anatomy
| Element | Description | |
|---|---|---|
| Header | required | Contains the title, optional subheader, and close button. |
| Body | required | Main content area of the popover. |
| Trigger Element | required | The button or link that toggles the popover open. |
Import
tsimport {XDSPopover} from '@xds/core/Popover'
Props
| Prop | Type | Description |
|---|---|---|
contentrequired | ReactNode | Content to display inside the popover. |
children | ReactNode | Trigger element. Must contain a <button> or [role="button"] element. |
anchorRef | React.RefObject<HTMLElement> | External ref to use as the popover anchor in sibling mode. |
placement | LayerPlacement (default: 'below') | Position placement relative to the trigger. |
alignment | LayerAlignment (default: 'start') | Alignment along the placement axis. |
isOpen | boolean | Whether the popover is shown in controlled mode. |
onOpenChange | (isOpen: boolean) => void | Callback fired when the popover visibility changes. |
isEnabled | boolean (default: true) | When false, trigger interactions are ignored. |
width | number | string (default: 'auto') | Width of the popover container. |
label | string | Accessible label for the popover dialog. |
hasCloseButton | boolean (default: true) | Whether to include a hidden close button for accessibility. |
closeButtonLabel | string (default: 'Close popover') | Label for the hidden close button. |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
Sub-components
Popover is a compound component with 1 sub-component.XDSPopover
A click-triggered popover for displaying interactive content anchored to a trigger element.| Prop | Type | Description |
|---|---|---|
contentrequired | ReactNode | Content to display inside the popover. |
children | ReactNode | Trigger element. Must contain a <button> or [role="button"] element. |
anchorRef | React.RefObject<HTMLElement> | External ref to use as the popover anchor in sibling mode. |
placement | LayerPlacement (default: 'below') | Position placement relative to the trigger. |
alignment | LayerAlignment (default: 'start') | Alignment along the placement axis. |
isOpen | boolean | Whether the popover is shown in controlled mode. |
onOpenChange | (isOpen: boolean) => void | Callback fired when the popover visibility changes. |
isEnabled | boolean (default: true) | When false, trigger interactions are ignored. |
width | number | string (default: 'auto') | Width of the popover container. |
label | string | Accessible label for the popover dialog. |
hasCloseButton | boolean (default: true) | Whether to include a hidden close button for accessibility. |
closeButtonLabel | string (default: 'Close popover') | Label for the hidden close button. |
xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value — not an inline style object like style={{}}. |
Examples
Common configurations, variations, and states.Popover — Confirm ActionInline confirmation popover for destructive actions with delete and cancel buttons.
tsx'use client';import {useState} from 'react';import {XDSPopover} from '@xds/core/Popover';import {XDSButton} from '@xds/core/Button';import {XDSVStack, XDSHStack} from '@xds/core/Layout';import {XDSHeading, XDSText} from '@xds/core/Text';export default function PopoverConfirmAction() {const [isOpen, setIsOpen] = useState(false);return (<XDSPopoverplacement="below"label="Confirm deletion"width={300}isOpen={isOpen}onOpenChange={setIsOpen}content={<XDSVStack gap={3}><XDSHeading level={4}>Delete project?</XDSHeading><XDSText type="body">This will permanently delete the project and all its data. Thisaction cannot be undone.</XDSText><XDSHStack gap={2} hAlign="end"><XDSButtonlabel="Delete"variant="destructive"onClick={() => setIsOpen(false)}>Delete</XDSButton><XDSButtonlabel="Cancel"variant="ghost"onClick={() => setIsOpen(false)}>Cancel</XDSButton></XDSHStack></XDSVStack>}><XDSButton label="Delete project" variant="destructive">Delete project</XDSButton></XDSPopover>);}
Popover — Filter PanelPopover with checkbox filters and apply/reset actions.
tsx'use client';import {useState} from 'react';import {XDSPopover} from '@xds/core/Popover';import {XDSButton} from '@xds/core/Button';import {XDSVStack, XDSHStack} from '@xds/core/Layout';import {XDSHeading} from '@xds/core/Text';import {XDSCheckboxInput} from '@xds/core/CheckboxInput';import {XDSDivider} from '@xds/core/Divider';export default function PopoverFilterPanel() {const [isOpen, setIsOpen] = useState(false);const [filters, setFilters] = useState({active: true,archived: false,drafts: true,shared: false,});const toggle = (key: keyof typeof filters) =>setFilters(prev => ({...prev, [key]: !prev[key]}));return (<XDSPopoverplacement="below"label="Filter"width={240}isOpen={isOpen}onOpenChange={setIsOpen}content={<XDSVStack gap={3}><XDSHeading level={4}>Filter by status</XDSHeading><XDSDivider /><XDSCheckboxInputlabel="Active"value={filters.active}onChange={() => toggle('active')}/><XDSCheckboxInputlabel="Archived"value={filters.archived}onChange={() => toggle('archived')}/><XDSCheckboxInputlabel="Drafts"value={filters.drafts}onChange={() => toggle('drafts')}/><XDSCheckboxInputlabel="Shared with me"value={filters.shared}onChange={() => toggle('shared')}/><XDSDivider /><XDSHStack gap={2} hAlign="end"><XDSButtonlabel="Apply"variant="primary"onClick={() => setIsOpen(false)}>Apply</XDSButton><XDSButtonlabel="Reset"variant="ghost"onClick={() =>setFilters({active: true,archived: false,drafts: true,shared: false,})}>Reset</XDSButton></XDSHStack></XDSVStack>}><XDSButton label="Filter">Filter</XDSButton></XDSPopover>);}
Popover — Keyboard ShortcutsPopover displaying a list of keyboard shortcuts with key and description pairs.
tsx'use client';import {XDSPopover} from '@xds/core/Popover';import {XDSButton} from '@xds/core/Button';import {XDSVStack, XDSHStack} from '@xds/core/Layout';import {XDSHeading, XDSText} from '@xds/core/Text';import {XDSDivider} from '@xds/core/Divider';const shortcuts = [{key: '⌘K', action: 'Command palette'},{key: '⌘/', action: 'Toggle sidebar'},{key: '⌘.', action: 'Quick actions'},];export default function PopoverKeyboardShortcuts() {return (<XDSPopoverplacement="below"label="Keyboard shortcuts"width={260}content={<XDSVStack gap={2}><XDSHeading level={4}>Keyboard shortcuts</XDSHeading><XDSDivider />{shortcuts.map(s => (<XDSHStack key={s.key} gap={3}><XDSText type="body" weight="bold">{s.key}</XDSText><XDSText type="body">{s.action}</XDSText></XDSHStack>))}</XDSVStack>}><XDSButton label="Shortcuts">Shortcuts</XDSButton></XDSPopover>);}
Popover — Settings PanelPopover with toggle switches for managing user preferences like notifications, dark mode, and sounds.
tsx'use client';import {useState} from 'react';import {XDSPopover} from '@xds/core/Popover';import {XDSButton} from '@xds/core/Button';import {XDSVStack} from '@xds/core/Layout';import {XDSHeading} from '@xds/core/Text';import {XDSSwitch} from '@xds/core/Switch';import {XDSDivider} from '@xds/core/Divider';export default function PopoverSettingsPanel() {const [notifications, setNotifications] = useState(true);const [darkMode, setDarkMode] = useState(false);const [sounds, setSounds] = useState(true);return (<XDSPopoverplacement="below"label="Settings"width={280}content={<XDSVStack gap={3}><XDSHeading level={4}>Settings</XDSHeading><XDSDivider /><XDSSwitchlabel="Notifications"description="Receive push notifications"value={notifications}onChange={setNotifications}/><XDSSwitchlabel="Dark mode"description="Use dark color theme"value={darkMode}onChange={setDarkMode}/><XDSSwitchlabel="Sounds"description="Play sounds for actions"value={sounds}onChange={setSounds}/></XDSVStack>}><XDSButton label="Settings">Settings</XDSButton></XDSPopover>);}
Showcase source
tsx'use client';import {useState} from 'react';import {XDSPopover} from '@xds/core/Popover';import {XDSButton} from '@xds/core/Button';import {XDSVStack} from '@xds/core/Layout';import {XDSText, XDSHeading} from '@xds/core/Text';import {XDSDivider} from '@xds/core/Divider';export default function PopoverShowcase() {const [isOpen, setIsOpen] = useState(true);return (<XDSPopoverisOpen={isOpen}onOpenChange={setIsOpen}placement="below"label="Settings"width={280}content={<XDSVStack gap={3}><XDSHeading level={4} tabIndex={0}>Settings</XDSHeading><XDSDivider /><XDSText type="body">Notifications, dark mode, and sound preferences.</XDSText></XDSVStack>}><XDSButton label="Settings">Settings</XDSButton></XDSPopover>);}