Popover
- Stable
- WCAG 2.2 evidence
- RTL
A short, non-modal panel anchored to a trigger. Built on React Aria DialogTrigger and Popover
(isNonModal), with a role="dialog" panel: the trigger exposes aria-expanded and aria-controls;
focus moves into the panel when it opens and back to the trigger when Escape, the close button or a slot="close"
action closes it (WCAG 2.1.1, 2.4.3, 4.1.2). Nothing is trapped: Tab past the last control leaves the panel for the
element after the trigger and closes it, a click outside closes it, and the rest of the page stays available
(WCAG 2.1.2). Placement flips and the panel stays inside the viewport. Consumer duties: content wording; for blocking decisions use a modal Dialog, for lists of actions use Menu.
import { Popover } from "@nexera-ui/react";- 6
- examples
- 26
- props
- 4
- live controls
- 1
- platform
- 11
- WCAG criteria
- 1
- block uses it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { Popover } from "@nexera-ui/react";
<Popover />Examples 5
The same examples as Storybook, rendered live. Open Code to copy one.
With header
Figma Variant=With header: a title (level 2 heading, the dialog's name) and a close button.
import { Button, Popover } from "@nexera-ui/react";
<Popover
body="3 people are on leave and 2 are working remotely. Sara Malik returns on Monday."
defaultOpen
variant="with-header"
title="Who is off today"
>
{<Button variant="secondary">Who is off</Button>}
</Popover>;
With actions
Figma Variant=With actions: header plus a ghost and a primary small Button. slot="close" makes an action close the popover and return focus to the trigger.
import { Button, Popover } from "@nexera-ui/react";
<Popover
body="3 people are on leave and 2 are working remotely. Sara Malik returns on Monday."
defaultOpen
variant="with-actions"
title="Approve 2 leave requests?"
primaryAction={
<Button size="sm" slot="close">
Approve
</Button>
}
secondaryAction={
<Button size="sm" variant="ghost" slot="close">
Later
</Button>
}
>
{<Button variant="secondary">Who is off</Button>}
</Popover>;
Placements
placement on each side of the trigger, all open (isOpen). Each panel flips to the other side when there is no room and stays 12 px inside the viewport.
import { Button, Popover } from "@nexera-ui/react";
const placements = ["top", "bottom", "left", "right"] as const;
const BODY = "3 people are on leave and 2 are working remotely. Sara Malik returns on Monday.";
export function Placements() {
return (
<div className="flex w-max flex-col">
{placements.map((placement) => (
<div key={placement} className="flex min-h-[16rem] w-[44rem] items-center justify-center">
<Popover
variant="with-header"
title={`placement="${placement}"`}
body={BODY}
placement={placement}
isOpen
>
<Button variant="secondary">{placement}</Button>
</Popover>
</div>
))}
</div>
);
}
Long content
Long titles, body text and action labels wrap; long content scrolls inside the panel when it reaches the viewport edge.
import { Button, Popover } from "@nexera-ui/react";
const BODY = "3 people are on leave and 2 are working remotely. Sara Malik returns on Monday.";
<Popover
body={`${BODY} ${BODY} Everyone else on the team is working as usual, and the next payroll run is on the 28th, so approving now leaves enough time to plan cover.`}
defaultOpen
variant="with-actions"
title="Approve the leave requests of the whole payroll team for the last week of December?"
primaryAction={
<Button size="sm" slot="close">
Approve all requests
</Button>
}
secondaryAction={
<Button size="sm" variant="ghost" slot="close">
Review one by one
</Button>
}
>
{<Button variant="secondary">Who is off</Button>}
</Popover>;
Right to left
Right-to-left: the close button and actions move to the left; placement="start"/"end" follow the reading direction.
import { Button, Popover } from "@nexera-ui/react";
<Popover
body="ثلاثة أشخاص في إجازة واثنان يعملان عن بُعد."
defaultOpen
variant="with-actions"
title="من في إجازة اليوم"
closeLabel="إغلاق"
primaryAction={
<Button size="sm" slot="close">
موافقة
</Button>
}
secondaryAction={
<Button size="sm" variant="ghost" slot="close">
لاحقًا
</Button>
}
>
{<Button variant="secondary">من في إجازة</Button>}
</Popover>;
Props 26
Press "Try it" on a card to load that prop into the playground.
26 props shown
children*NexeraReactNodeThe trigger: one pressable element, usually a Nexera Button. It gets aria-expanded and aria-controls, toggles
the popover when pressed and receives focus back when the popover closes.
body*NexeraReactNodeMain text. Also the dialog's description (aria-describedby).
isOpenNexerabooleanWhether the popover is open (controlled). Use with onOpenChange.
defaultOpenNexerabooleanWhether the popover is open on first render (uncontrolled).
onOpenChangeNexera(isOpen: boolean) => voidCalled when the popover opens or closes.
closeLabelNexerastringAccessible name of the close button. Translate it for your locale.
classNameNexerastringExtra classes for the surface, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style for the surface.
variantNexera"default" | "with-header" | "with-actions"Panel layout: default is the body only; with-header adds a title and a close button;
with-actions adds a row of actions under the body.
titleNexeraReactNodeTitle, a level 2 heading that names the dialog.
primaryActionNexeraReactNodeMain action at the end of the row: <Button size="sm">. Give it slot="close" to close
the popover when pressed.
secondaryActionNexeraReactNodeSecondary action before it: <Button variant="ghost" size="sm">. Shown when provided.
aria-labelNexerastringAccessible name of the dialog. Without it the dialog is named by the trigger's label.
placementReact Aria"bottom" | "bottom left" | "bottom right" | "bottom start" | "bottom end" | "top" | "top left" | "top right" | "top start" | "top end" | "left" | "left top" | "left bottom" | "start" | "start top" | "start bottom" | "right" | "right top" | "right bottom" | "end" | "end top" | "end bottom"The placement of the element with respect to its anchor element.
containerPaddingReact ArianumberThe placement padding that should be applied between the element and its surrounding container.
offsetReact ArianumberThe additional offset applied along the main axis between the element and its anchor element.
crossOffsetReact ArianumberThe additional offset applied along the cross axis between the element and its anchor element.
shouldFlipReact AriabooleanWhether the element should flip its orientation (e.g. top to bottom or left to right) when there is insufficient room for it to render completely.
boundaryElementReact AriaElementElement that that serves as the positioning boundary.
scrollRefReact AriaRefObject<Element | null>A ref for the scrollable region within the overlay.
shouldUpdatePositionReact AriabooleanWhether the overlay should update its position automatically.
maxHeightReact ArianumberThe maxHeight specified for the overlay element. By default, it will take all space up to the current viewport height.
arrowBoundaryOffsetReact ArianumberThe minimum distance the arrow's edge should be from the edge of the overlay element.
getTargetRectReact Aria(target: Element) => DOMRect | nullOverrides the target element's bounding rectangle. Useful for positioning relative to a specific point such as the mouse cursor (e.g. context menus) or text selection. @param target - The target element.
isKeyboardDismissDisabledReact AriabooleanWhether pressing the escape key to close the popover should be disabled. Most popovers should not use this option. When set to true, an alternative way to close the popover with a keyboard must be provided.
shouldCloseOnInteractOutsideReact Aria(element: Element) => booleanWhen user interacts with the argument element outside of the popover ref, return true if onClose should be called. This gives you a chance to filter out interaction with elements that should not dismiss the popover. By default, onClose will always be called on interaction outside the popover ref.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
5 direct · 6 supporting- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.3.2Meaningful SequenceLevel A · supporting test
- 1.4.10ReflowLevel AA · supporting test
- 1.4.12Text SpacingLevel AA · supporting test
- 2.1.1KeyboardLevel A · tested directly
- 2.1.2No Keyboard TrapLevel A · tested directly
- 2.3.3Animation from InteractionsLevel AAA · supporting test
- 2.4.3Focus OrderLevel A · tested directly
- 2.4.7Focus VisibleLevel AA · supporting test
- 2.5.8Target Size (Minimum)Level AA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
data-hovereddata-pressed
<Popover className="data-hovered:opacity-90 shadow-sm" />Used in blocks
Related components
- ActionSheetAn iOS-style sheet of actions that slides up from the bottom, with a separate Cancel row.
- ActionSheetItemOne action in an `ActionSheet`.
- BottomSheetA mobile sheet that slides up from the bottom edge for a short task or a set of options.
- CommandPaletteA searchable list of pages, actions and people in a modal.
- DialogA short modal question that needs a decision before work continues, such as "Submit October payroll?".
- DrawerA side panel over the page for details, filters or a secondary task.
- ModalA modal window for a task that needs the user's full attention, such as a short form.
- TooltipA short description of a control, shown on hover and keyboard focus.