Popover
Popover displays rich content in a floating panel triggered by a click.
Import
import { Popover, PopoverHeader, PopoverBody } from "@tosui/react";
Basic Usage
- Code
- Preview
<Popover
content={
<PopoverBody>
This is the popover content.
</PopoverBody>
}
>
<Button>Click me</Button>
</Popover>
With Header
- Code
- Preview
<Popover
content={
<>
<PopoverHeader>Popover Title</PopoverHeader>
<PopoverBody>
This popover has a header and body.
</PopoverBody>
</>
}
>
<Button>With Header</Button>
</Popover>
Placement
- Code
- Preview
<HStack gap={4}>
<Popover placement="top" content={<PopoverBody>Top</PopoverBody>}>
<Button>Top</Button>
</Popover>
<Popover placement="bottom" content={<PopoverBody>Bottom</PopoverBody>}>
<Button>Bottom</Button>
</Popover>
<Popover placement="left" content={<PopoverBody>Left</PopoverBody>}>
<Button>Left</Button>
</Popover>
<Popover placement="right" content={<PopoverBody>Right</PopoverBody>}>
<Button>Right</Button>
</Popover>
</HStack>
Close on Blur
By default, popover closes when clicking outside. Disable with closeOnBlur={false}.
<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
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
content={
<>
<PopoverHeader>Delete item?</PopoverHeader>
<PopoverBody>
<Text mb={3}>This action cannot be undone.</Text>
<HStack gap={2} justify="flex-end">
<Button size="sm" variant="ghost">Cancel</Button>
<Button size="sm" color="error">Delete</Button>
</HStack>
</PopoverBody>
</>
}
>
<Button variant="ghost" color="error">Delete</Button>
</Popover>
User Profile Popover
<Popover
content={
<PopoverBody>
<VStack gap={2} align="flex-start">
<HStack gap={3}>
<Avatar name="John Doe" />
<Box>
<Text fontWeight="medium">John Doe</Text>
<Text fontSize="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 color="error">Sign Out</Button>
</VStack>
</PopoverBody>
}
>
<Avatar name="John Doe" />
</Popover>
Form Popover
<Popover
closeOnBlur={false}
content={
<>
<PopoverHeader>Quick Add</PopoverHeader>
<PopoverBody>
<VStack gap={3}>
<Input placeholder="Item name" />
<HStack gap={2} justify="flex-end">
<Button size="sm" variant="ghost">Cancel</Button>
<Button size="sm">Add</Button>
</HStack>
</VStack>
</PopoverBody>
</>
}
>
<Button>Quick Add</Button>
</Popover>
Props Reference
Popover
| Prop | Type | Default | Description |
|---|---|---|---|
| content | ReactNode | - | Popover content (required) |
| placement | "top" | "bottom" | "left" | "right" | "bottom" | Popover position |
| closeOnBlur | boolean | true | Close on outside click |
| isOpen | boolean | - | Controlled open state |
| onOpen | () => void | - | Callback when popover opens |
| onClose | () => void | - | Callback when popover closes |
| children | ReactNode | - | Trigger element (required) |
PopoverHeader, PopoverBody
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Additional CSS class |
| children | ReactNode | - | Section content |
Accessibility
- Uses
role="dialog"on popover - Trigger has
aria-expandedandaria-haspopup="dialog" - Closes on Escape key
- Renders via portal to avoid clipping
TypeScript
import { Popover, PopoverHeader, PopoverBody, type PopoverPlacement, type PopoverProps, type PopoverHeaderProps, type PopoverBodyProps } from "@tosui/react";