Displays rich content in a portal, triggered by a button.
import React from 'react';
import * as Popover from '@radix-ui/react-popover';
import { MixerHorizontalIcon, Cross2Icon } from '@radix-ui/react-icons';
import './styles.css';
const PopoverDemo = () => (
<Popover.Root>
<Popover.Trigger asChild>
<button className="IconButton" aria-label="Update dimensions">
<MixerHorizontalIcon />
</button>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content className="PopoverContent" sideOffset={5}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
<p className="Text" style={{ marginBottom: 10 }}>
Dimensions
</p>
<fieldset className="Fieldset">
<label className="Label" htmlFor="width">
Width
</label>
<input className="Input" id="width" defaultValue="100%" />
</fieldset>
<fieldset className="Fieldset">
<label className="Label" htmlFor="maxWidth">
Max. width
</label>
<input className="Input" id="maxWidth" defaultValue="300px" />
</fieldset>
<fieldset className="Fieldset">
<label className="Label" htmlFor="height">
Height
</label>
<input className="Input" id="height" defaultValue="25px" />
</fieldset>
<fieldset className="Fieldset">
<label className="Label" htmlFor="maxHeight">
Max. height
</label>
<input className="Input" id="maxHeight" defaultValue="none" />
</fieldset>
</div>
<Popover.Close className="PopoverClose" aria-label="Close">
<Cross2Icon />
</Popover.Close>
<Popover.Arrow className="PopoverArrow" />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
export default PopoverDemo;
Install the component from your command line.
npm install @radix-ui/react-popover
Import all parts and piece them together.
import * as Popover from '@radix-ui/react-popover';
export default () => (
<Popover.Root>
<Popover.Trigger />
<Popover.Anchor />
<Popover.Portal>
<Popover.Content>
<Popover.Close />
<Popover.Arrow />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
Contains all the parts of a popover.
Prop | Type | Default |
---|---|---|
defaultOpen | boolean | |
open | boolean | |
onOpenChange | function | |
modal | boolean | false |
The button that toggles the popover. By default, the Popover.Content
will position itself against the trigger.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "open" | "closed" |
An optional element to position the Popover.Content
against. If this part is not used, the content will position alongside the Popover.Trigger
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
When used, portals the content part into the body
.
Prop | Type | Default |
---|---|---|
forceMount | boolean | |
container | HTMLElement | document.body |
The component that pops out when the popover is open.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
onOpenAutoFocus | function | |
onCloseAutoFocus | function | |
onEscapeKeyDown | function | |
onPointerDownOutside | function | |
onFocusOutside | function | |
onInteractOutside | function | |
forceMount | boolean | |
side | enum | "bottom" |
sideOffset | number | 0 |
align | enum | "center" |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-popover-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
An optional arrow element to render alongside the popover. This can be used to help visually link the anchor with the Popover.Content
. Must be rendered inside Popover.Content
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
width | number | 10 |
height | number | 5 |
The button that closes an open popover.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
We expose a CSS custom property --radix-popover-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
// index.jsx
import * as Popover from '@radix-ui/react-popover';
import './styles.css';
export default () => (
<Popover.Root>
<Popover.Trigger>…</Popover.Trigger>
<Popover.Portal>
<Popover.Content className="PopoverContent">…</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
/* styles.css */
.PopoverContent {
transform-origin: var(--radix-popover-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
// index.jsx
import * as Popover from '@radix-ui/react-popover';
import './styles.css';
export default () => (
<Popover.Root>
<Popover.Trigger>…</Popover.Trigger>
<Popover.Portal>
<Popover.Content className="PopoverContent">…</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
/* styles.css */
.PopoverContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.PopoverContent[data-side='top'] {
animation-name: slideUp;
}
.PopoverContent[data-side='bottom'] {
animation-name: slideDown;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
You can anchor the content to another element if you do not want to use the trigger as the anchor.
// index.jsx
import * as Popover from '@radix-ui/react-popover';
import './styles.css';
export default () => (
<Popover.Root>
<Popover.Anchor asChild>
<div className="Row">
Row as anchor <Popover.Trigger>Trigger</Popover.Trigger>
</div>
</Popover.Anchor>
<Popover.Portal>
<Popover.Content>…</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
/* styles.css */
.Row {
background-color: gainsboro;
padding: 20px;
}
Adheres to the Dialog WAI-ARIA design pattern.
Key | Description |
---|---|
Space | Opens/closes the popover. |
Enter | Opens/closes the popover. |
Tab | Moves focus to the next focusable element |
Shift + Tab | Moves focus to the previous focusable element |
Esc | Closes the popover and moves focus to Popover.Trigger . |
Create your own API by abstracting the primitive parts into your own component.
This example abstracts the Popover.Arrow
part and sets a default sideOffset
configuration.
import { Popover, PopoverTrigger, PopoverContent } from './your-popover';
export default () => (
<Popover>
<PopoverTrigger>Popover trigger</PopoverTrigger>
<PopoverContent>Popover content</PopoverContent>
</Popover>
);
// your-popover.jsx
import React from 'react';
import * as PopoverPrimitive from '@radix-ui/react-popover';
export const Popover = PopoverPrimitive.Root;
export const PopoverTrigger = PopoverPrimitive.Trigger;
export const PopoverContent = React.forwardRef(
({ children, ...props }, forwardedRef) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content sideOffset={5} {...props} ref={forwardedRef}>
{children}
<PopoverPrimitive.Arrow />
</PopoverPrimitive.Content>
</PopoverPrimitive.Portal>
)
);