Skip to main content

Popover

Popover displays rich content in a floating panel triggered by a click.

Open in Storybook

Import​

import { Popover, PopoverHeader, PopoverBody } from "@tosui/react";

Basic usage​

With header​

Accessible name​

Give every popover an accessible name. Reference a visible heading with aria-labelledby, as shown above. If the panel has no visible heading, use aria-label.

The child must accept event and ARIA props so Popover can connect it to the panel. Use a Button, IconButton, or another focusable component that forwards those props.

Placement​

Close on blur​

By default, popover closes when clicking outside. Disable with closeOnBlur={false}.

<Popover
aria-label="Persistent popover"
closeOnBlur={false}
content={
<PopoverBody>
Click outside - I won't close!
</PopoverBody>
}
>
<Button>Stays Open</Button>
</Popover>

Controlled mode​

Use isOpen, onOpen, and onClose for full control.

function ControlledPopover() {
const [isOpen, setIsOpen] = useState(false);

return (
<Popover
aria-label="Controlled popover"
isOpen={isOpen}
onOpen={() => setIsOpen(true)}
onClose={() => setIsOpen(false)}
content={
<PopoverBody>
<Button size="sm" onClick={() => setIsOpen(false)}>
Close
</Button>
</PopoverBody>
}
>
<Button>Controlled</Button>
</Popover>
);
}

Common patterns​

Confirmation popover​

<Popover
aria-label="Delete item"
content={
<>
<PopoverHeader>Delete item?</PopoverHeader>
<PopoverBody>
<Text mb={3}>This action cannot be undone.</Text>
<HStack gap={2} justify="end">
<Button size="sm" variant="ghost">Cancel</Button>
<Button size="sm" colorScheme="error">Delete</Button>
</HStack>
</PopoverBody>
</>
}
>
<Button variant="ghost" colorScheme="error">Delete</Button>
</Popover>

User profile Popover​

<Popover
aria-label="John Doe profile"
content={
<PopoverBody>
<VStack gap={2} align="start">
<HStack gap={3}>
<Avatar name="John Doe" />
<Box>
<Text weight="medium">John Doe</Text>
<Text size="sm" color="foreground-muted">john@example.com</Text>
</Box>
</HStack>
<Divider />
<Button variant="ghost" size="sm" fullWidth>View Profile</Button>
<Button variant="ghost" size="sm" fullWidth>Settings</Button>
<Button variant="ghost" size="sm" fullWidth colorScheme="error">Sign Out</Button>
</VStack>
</PopoverBody>
}
>
<Button variant="ghost" aria-label="Open John Doe profile">
<Avatar name="John Doe" />
</Button>
</Popover>

Form popover​

<Popover
aria-label="Quick add"
closeOnBlur={false}
content={
<>
<PopoverHeader>Quick Add</PopoverHeader>
<PopoverBody>
<VStack gap={3}>
<Input placeholder="Item name" />
<HStack gap={2} justify="end">
<Button size="sm" variant="ghost">Cancel</Button>
<Button size="sm">Add</Button>
</HStack>
</VStack>
</PopoverBody>
</>
}
>
<Button>Quick Add</Button>
</Popover>

Props reference​

Popover​

PropTypeDefaultDescription
contentReactNode-Popover content (required)
placement"top" | "bottom" | "left" | "right""bottom"Popover position
closeOnBlurbooleantrueClose on outside click
isOpenboolean-Controlled open state
onOpen() => void-Callback when popover opens
onClose() => void-Callback when popover closes
classNamestring-Additional CSS class on the dialog panel
aria-labelstring-Accessible name for the dialog
aria-labelledbystring-ID of an element that labels the dialog
childrenReactNode-Focusable trigger element (required)

PopoverHeader, PopoverBody​

PropTypeDefaultDescription
classNamestring-Additional CSS class
childrenReactNode-Section content

Accessibility​

  • The panel uses role="dialog".
  • Give every popover an accessible name with aria-label or aria-labelledby.
  • The focusable trigger receives aria-expanded, aria-haspopup="dialog", and aria-controls. Popover preserves other trigger attributes and adds the panel ID to an existing aria-controls value.
  • Focus moves into the panel, stays inside it, and returns to the previously focused element when the panel closes.
  • Escape closes the panel.
  • The portal renders only in the browser and updates its position during scroll and resize events.

TypeScript​

import { Popover, PopoverHeader, PopoverBody, type PopoverPlacement, type PopoverProps, type PopoverHeaderProps, type PopoverBodyProps } from "@tosui/react";