📄 radixui/primitives/docs/components/popover

File: popover.md | Updated: 11/15/2025

Source: https://www.radix-ui.com/primitives/docs/components/popover

Radix Homepage

Made by WorkOS

Radix Homepage

Made by WorkOS

ThemesThemes PrimitivesPrimitives IconsIcons ColorsColors

Documentation Case studies Blog

Search

/

Overview

Introduction Getting started Accessibility Releases

Guides

Styling Animation Composition Server-side rendering

Components

Accordion Alert Dialog Aspect Ratio Avatar Checkbox Collapsible Context Menu Dialog Dropdown Menu Form

Preview
Hover Card Label Menubar Navigation Menu One-Time Password Field

Preview
Password Toggle Field

Preview
Popover Progress Radio Group Scroll Area Select Separator Slider Switch Tabs Toast Toggle Toggle Group Toolbar Tooltip

Utilities

Accessible Icon Direction Provider Portal Slot Visually Hidden

Components

Popover

Displays rich content in a portal, triggered by a button.

index.jsxindex.jsxstyles.cssstyles.css

CSS

import * as React from "react";
import { Popover } from "radix-ui";
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;

Features

Can be controlled or uncontrolled.

Customize side, alignment, offsets, collision handling.

Optionally render a pointing arrow.

Focus is fully managed and customizable.

Supports modal and non-modal modes.

Dismissing and layering behavior is highly customizable.

Installation


Install the component from your command line.

npm install @radix-ui/react-popover

Anatomy


Import all parts and piece them together.

import { Popover } from "radix-ui";

export default () => (
	<Popover.Root>
		<Popover.Trigger />
		<Popover.Anchor />
		<Popover.Portal>
			<Popover.Content>
				<Popover.Close />
				<Popover.Arrow />
			</Popover.Content>
		</Popover.Portal>
	</Popover.Root>
);

API Reference


Root

Contains all the parts of a popover.

| Prop | Type | Default | | --- | --- | --- | | defaultOpen<br><br>Prop description | boolean | No default value | | open<br><br>Prop description | boolean | No default value | | onOpenChange<br><br>Prop description | function<br><br>See full type | No default value | | modal<br><br>Prop description | boolean | false |

Trigger

The button that toggles the popover. By default, the Popover.Content will position itself against the trigger.

| Prop | Type | Default | | --- | --- | --- | | asChild<br><br>Prop description | boolean | false |

| Data attribute | Values | | --- | --- | | [data-state] | "open" \| "closed" |

Anchor

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<br><br>Prop description | boolean | false |

Portal

When used, portals the content part into the body.

| Prop | Type | Default | | --- | --- | --- | | forceMount<br><br>Prop description | boolean | No default value | | container<br><br>Prop description | HTMLElement | document.body |

Content

The component that pops out when the popover is open.

| Prop | Type | Default | | --- | --- | --- | | asChild<br><br>Prop description | boolean | false | | onOpenAutoFocus<br><br>Prop description | function<br><br>See full type | No default value | | onCloseAutoFocus<br><br>Prop description | function<br><br>See full type | No default value | | onEscapeKeyDown<br><br>Prop description | function<br><br>See full type | No default value | | onPointerDownOutside<br><br>Prop description | function<br><br>See full type | No default value | | onFocusOutside<br><br>Prop description | function<br><br>See full type | No default value | | onInteractOutside<br><br>Prop description | function<br><br>See full type | No default value | | forceMount<br><br>Prop description | boolean | No default value | | side<br><br>Prop description | enum<br><br>See full type | "bottom" | | sideOffset<br><br>Prop description | number | 0 | | align<br><br>Prop description | enum<br><br>See full type | "center" | | alignOffset<br><br>Prop description | number | 0 | | avoidCollisions<br><br>Prop description | boolean | true | | collisionBoundary<br><br>Prop description | Boundary<br><br>See full type | [] | | collisionPadding<br><br>Prop description | number \| Padding<br><br>See full type | 0 | | arrowPadding<br><br>Prop description | number | 0 | | sticky<br><br>Prop description | enum<br><br>See full type | "partial" | | hideWhenDetached<br><br>Prop description | 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 | | --radix-popover-content-available-width | The remaining width between the trigger and the boundary edge | | --radix-popover-content-available-height | The remaining height between the trigger and the boundary edge | | --radix-popover-trigger-width | The width of the trigger | | --radix-popover-trigger-height | The height of the trigger |

Arrow

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<br><br>Prop description | boolean | false | | width<br><br>Prop description | number | 10 | | height<br><br>Prop description | number | 5 |

Close

The button that closes an open popover.

| Prop | Type | Default | | --- | --- | --- | | asChild<br><br>Prop description | boolean | false |

Examples


Constrain the content size

You may want to constrain the width of the content so that it matches the trigger width. You may also want to constrain its height to not exceed the viewport.

We expose several CSS custom properties such as --radix-popover-trigger-width and --radix-popover-content-available-height to support this. Use them to constrain the content dimensions.

// index.jsx
import { Popover } from "radix-ui";
import "./styles.css";

export default () => (
	<Popover.Root>
		<Popover.Trigger>…</Popover.Trigger>
		<Popover.Portal>
			<Popover.Content className="PopoverContent" sideOffset={5}>
				…
			</Popover.Content>
		</Popover.Portal>
	</Popover.Root>
);


/* styles.css */
.PopoverContent {
	width: var(--radix-popover-trigger-width);
	max-height: var(--radix-popover-content-available-height);
}

Origin-aware animations

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 { Popover } from "radix-ui";
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);
	}
}

Collision-aware animations

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 { Popover } from "radix-ui";
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);
	}
}

With custom anchor

You can anchor the content to another element if you do not want to use the trigger as the anchor.

// index.jsx
import { Popover } from "radix-ui";
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;
}

Accessibility


Adheres to the Dialog WAI-ARIA design pattern .

Keyboard Interactions

| 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. |

Custom APIs


Create your own API by abstracting the primitive parts into your own component.

Abstract the arrow and set default configuration

This example abstracts the Popover.Arrow part and sets a default sideOffset configuration.

Usage

import { Popover, PopoverTrigger, PopoverContent } from "./your-popover";

export default () => (
	<Popover>
		<PopoverTrigger>Popover trigger</PopoverTrigger>
		<PopoverContent>Popover content</PopoverContent>
	</Popover>
);

Implementation

// your-popover.jsx
import * as React from "react";
import { Popover as PopoverPrimitive } from "radix-ui";

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>
	),
);

PreviousPassword Toggle Field

NextProgress

Edit this page on GitHub.