Popover
Install: npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1
Version and compatibility
Pinned Mantine Core 9.7.1 requires React and React DOM ^19.2.0 and matching @mantine/hooks@9.7.1, from the installed npm package's peerDependencies. Do not force it into React 18 or another incompatible version. Explain upgrade impact and agree on a plan with the project owner; do not hide conflicts with --force/--legacy-peer-deps. The native Provider enables theme.respectReducedMotion and uses forceColorScheme for light/dark.
Use case
Show content anchored to a target.
Native props used by this example
These props are extracted from the local typechecked JSX and grouped by their owning native component. This is not an exhaustive prop list; see pinned source signatures and inheritance below.
This minimal example passes children only; no additional props are invented. Optional props are in the source reference.
Interaction and state checks
Current scenarios: Default. Check actual behavior after selecting supported disabled/error/read-only scenarios; components without those native states do not receive invented universal props. Verify keyboard focus and exits, then connect changed values through the native callback signature.
Business integration boundary
Demo content/state is local, without server persistence, authorization or fetching. A static display component is not a completed business workflow.
Host setup
Install pinned @mantine/core and @mantine/hooks, import @mantine/core/styles.css at the entry and use MantineProvider. The example's theme/variant are JGuang-authored host props, not universal native component APIs; forceColorScheme synchronizes the app theme. Native implementation source is a reading reference: use npm exports rather than copying internal engines. This batch covers Core, not dates/charts/forms extension packages. Detailed upstream prose remains English.
Runnable usage
import { useState } from 'react';
import * as M from '@mantine/core';
import '@mantine/core/styles.css';
function ComponentPreview({ variant = 'Default' }: { variant?: string }) {
const content = <M.Text>Build something beautiful / 构建美好的界面</M.Text>;
return <M.Popover><M.Popover.Target><M.Button>Toggle popover</M.Button></M.Popover.Target><M.Popover.Dropdown>{content}</M.Popover.Dropdown></M.Popover>;
}
export default function Example({ variant = 'Default', theme = 'light' }: { variant?: string; theme?: 'light' | 'dark' }) {
return <M.MantineProvider forceColorScheme={theme} theme={{ respectReducedMotion: true }}><ComponentPreview variant={variant} /></M.MantineProvider>;
}
Preview states
Default
The example uses the native Mantine API. For supported input demos, change the local variant to Disabled, Invalid or Read only; these are example labels, not new component props. Keep the documented data, controlled values, stores and compound children when adapting the example.
Usage
Controlled
You can control the Popover state with the opened and onChange props:
import { useState } from 'react';
import { Button, Popover } from '@mantine/core';
function Demo() {
const [opened, setOpened] = useState(false);
return (
<Popover opened={opened} onChange={setOpened}>
<Popover.Target>
<Button onClick={() => setOpened((o) => !o)}>
Toggle popover
</Button>
</Popover.Target>
<Popover.Dropdown>Dropdown</Popover.Dropdown>
</Popover>
);
}
Controlled example with mouse events:
Focus trap
If you need to use interactive elements (inputs, buttons, etc.) inside Popover.Dropdown, set the trapFocus prop:
Inline elements
Enable the inline middleware to use Popover with inline elements:
Same width
Set the width="target" prop to make the Popover dropdown take the same width as the target element:
offset
Set the offset prop to a number to change the dropdown position relative to the target element.
This way you can control the dropdown offset on the main axis only.
To control offset on both axes, pass an object with mainAxis and crossAxis properties:
Middlewares
You can enable or disable Floating UI middlewares with the
middlewares prop:
- shift middleware shifts the dropdown to keep it in view. It is enabled by default.
- flip middleware changes the placement of the dropdown to keep it in view. It is enabled by default.
- inline middleware improves positioning for inline reference elements that span over multiple lines. It is disabled by default.
- size middleware manipulates the dropdown size. It is disabled by default.
Example of turning off shift and flip middlewares:
import { Popover } from '@mantine/core';
function Demo() {
return (
<Popover
middlewares={{ flip: false, shift: false }}
position="bottom"
>
{/* Popover content */}
</Popover>
);
}
Customize middleware options
To customize Floating UI middleware options, pass them as
an object to the middlewares prop. For example, to change the shift
middleware padding to 20px, use the following configuration:
import { Popover } from '@mantine/core';
function Demo() {
return (
<Popover
middlewares={{ shift: { padding: 20 } }}
position="bottom"
>
{/* Popover content */}
</Popover>
);
}
Dropdown arrow
Set the withArrow prop to add an arrow to the dropdown. The arrow is a div element rotated with transform: rotate(45deg).
The arrowPosition prop determines how the arrow is positioned relative to the target element when position is set to *-start and *-end values on the Popover component.
By default, the value is center – the arrow is positioned in the center of the target element if it is possible.
If you change arrowPosition to side, then the arrow will be positioned on the side of the target element,
and you will be able to control the arrow offset with the arrowOffset prop. Note that when arrowPosition is set to center,
the arrowOffset prop is ignored.
With overlay
Set the withOverlay prop to add an overlay behind the dropdown. You can pass additional
configuration to the Overlay component with the overlayProps prop:
Hide detached
Use the hideDetached prop to configure how the dropdown behaves when the target
element is hidden with styles (display: none, visibility: hidden, etc.),
removed from the DOM, or when the target element is scrolled out of the viewport.
By default, hideDetached is enabled – the dropdown is hidden with the target element.
You can change this behavior with hideDetached={false}. To see the difference, try to scroll
the root element of the following demo:
Disabled
Set the disabled prop to prevent Popover.Dropdown from rendering:
Click outside
By default, Popover closes when you click outside of the dropdown. To disable this behavior, set closeOnClickOutside={false}.
If an element outside of the popover stops propagation of the event, the popover does not close when that element is clicked.
Set the clickOutsideCapture prop to register listeners in the capture phase and close the popover in this case as well.
You can configure events that are used for click-outside detection with the clickOutsideEvents prop.
By default, Popover listens to mousedown and touchstart events. You can change it to any other
events, for example, mouseup and touchend:
onDismiss
If you need to control the opened state, but still want to close the popover on outside clicks
and escape key presses, use the onDismiss prop:
import { useState } from 'react';
import { Button, Popover } from '@mantine/core';
function Demo() {
const [opened, setOpened] = useState(false);
return (
<Popover
opened={opened}
onDismiss={() => setOpened(false)}
>
<Popover.Target>
<Button onClick={() => setOpened((o) => !o)}>
Toggle popover
</Button>
</Popover.Target>
<Popover.Dropdown>Dropdown</Popover.Dropdown>
</Popover>
);
}
Initial focus
Popover uses the FocusTrap component to manage focus.
Add the data-autofocus attribute to the element that should receive initial focus:
import { Popover } from '@mantine/core';
function Demo() {
return (
<Popover>
<Popover.Target>
<button type="button">Target</button>
</Popover.Target>
<Popover.Dropdown>
<input />
<input data-autofocus />
<input />
</Popover.Dropdown>
</Popover>
);
}
Context menu
Use Popover.ContextMenu to open the dropdown at the cursor position on right-click.
It replaces Popover.Target and wraps the element that should respond to the contextmenu
event – the browser's default context menu is suppressed, and Popover.Dropdown is positioned
at the cursor. Right-clicking again repositions the dropdown to the new coordinates.
Popover.Dropdown can contain any content. Set disabled to restore the browser's default
context menu:
Touch devices
On touch devices (most notably iOS Safari, which does not fire the contextmenu event),
the dropdown is opened with a long-press instead. Use the longPressDelay prop to control
how long the element must be pressed before the dropdown opens, 500ms by default. To
prevent the native text-selection callout from appearing under the dropdown on touch
devices, Popover.ContextMenu disables text selection (user-select: none) on the wrapped
element.
Nested popovers
Nested popovers require children rendering without Portal. Usually, you
should disable the portal with props of the component that renders popover content. For example,
Select has a comboboxProps={{ withinPortal: false }} prop. Check the documentation
of the component that you are using to render popover content to find out how to disable the portal.
If the portal is not disabled, outside clicks will close all popovers.
Example of disabling the portal in Select and DatePickerInput components:
Accessibility
Popover follows WAI-ARIA recommendations:
- Dropdown element has
role="dialog"andaria-labelledby="target-id"attributes - Target element has
aria-haspopup="dialog",aria-expanded,aria-controls="dropdown-id"attributes
An uncontrolled Popover will be accessible only when used with a button element or component that renders it (Button, ActionIcon, etc.).
Other elements will not support Space and Enter key presses.
Keyboard interactions
Source-backed parameter reference
Implementation signatures and types are generated from the pinned local source. Optional props, native inherited types, union branches and never exclusions are preserved. A forwardRef implementation's ref is a React reference, not an additional business prop.
Popover(_props: PopoverProps)
export type PopoverStylesNames = 'dropdown' | 'arrow' | 'overlay';
export type PopoverCssVariables = {
dropdown: '--popover-radius' | '--popover-shadow';
};
export interface __PopoverProps {
/** Dropdown position relative to the target element @default 'bottom' */
position?: FloatingPosition;
/** Offset of the dropdown element @default 8 */
offset?: number | FloatingAxesOffsets;
/** Called when dropdown position changes */
onPositionChange?: (position: FloatingPosition) => void;
/** Called when dropdown closes */
onClose?: () => void;
/** Called when the popover is dismissed by clicking outside or by pressing escape */
onDismiss?: () => void;
/** Called when dropdown opens */
onOpen?: () => void;
/** If set, the dropdown is not unmounted from the DOM when hidden. `display: none` styles are added instead. */
keepMounted?: boolean;
/**
* Controls how the dropdown is hidden when `keepMounted` is set:
* `'activity'` – hidden with React 19 `Activity` component,
* `'display-none'` – hidden with `display: none` styles
* @default 'activity'
*/
keepMountedMode?: 'activity' | 'display-none';
/** Props passed down to the `Transition` component. Use to configure duration and animation type. @default { duration: 150, transition: 'fade' } */
transitionProps?: TransitionOverride;
/** Called when exit transition ends */
onExitTransitionEnd?: () => void;
/** Called when enter transition ends */
onEnterTransitionEnd?: () => void;
/** Dropdown width, or `'target'` to make dropdown width the same as target element @default 'max-content' */
width?: PopoverWidth;
/** Floating ui middlewares to configure position handling @default { flip: true, shift: true, inline: false } */
middlewares?: PopoverMiddlewares;
/** Determines whether component should have an arrow @default false */
withArrow?: boolean;
/** Determines whether the overlay should be displayed when the dropdown is opened @default false */
withOverlay?: boolean;
/** Props passed down to `Overlay` component */
overlayProps?: OverlayProps & ElementProps<'div'>;
/** Arrow size in px @default 7 */
arrowSize?: number;
/** Arrow offset in px @default 5 */
arrowOffset?: number;
/** Arrow `border-radius` in px @default 0 */
arrowRadius?: number;
/** Arrow position */
arrowPosition?: ArrowPosition;
/** Determines whether dropdown should be rendered within the `Portal` @default true */
withinPortal?: boolean;
/** Props to pass down to the `Portal` when `withinPortal` is true */
portalProps?: BasePortalProps;
/** Dropdown `z-index` @default 300 */
zIndex?: string | number;
/** Key of `theme.radius` or any valid CSS value to set border-radius @default theme.defaultRadius */
radius?: MantineRadius;
/** Key of `theme.shadows` or any other valid CSS `box-shadow` value */
shadow?: MantineShadow;
/** If set, popover dropdown will not be rendered */
disabled?: boolean;
/** Determines whether focus should be automatically returned to control when dropdown closes @default false */
returnFocus?: boolean;
/** Changes floating ui [position strategy](https://floating-ui.com/docs/usefloating#strategy) @default 'absolute' */
floatingStrategy?: FloatingStrategy;
/** If set, the dropdown is hidden when the element is hidden with styles or not visible on the screen @default true */
hideDetached?: boolean;
/** If `true`, the dropdown picks its side on open (flip runs once, preferring the `position` prop) and then never changes side — scrolling, resizing, and content size changes will not flip the dropdown. The side is recalculated fresh on the next open. Does not affect the `shift` middleware. Set to `false` to keep flip active and allow the dropdown to re-flip on every change. @default true */
preventPositionChangeWhenVisible?: boolean;
/** If set, outside click listeners are registered in the capture phase @default false */
clickOutsideCapture?: boolean;
}
export interface PopoverProps extends __PopoverProps, StylesApiProps<PopoverFactory> {
__staticSelector?: string;
/** `Popover.Target` and `Popover.Dropdown` components */
children?: React.ReactNode;
/** Initial opened state for uncontrolled component */
defaultOpened?: boolean;
/** Controlled dropdown opened state */
opened?: boolean;
/** Called with current state when dropdown opens or closes */
onChange?: (opened: boolean) => void;
/** Determines whether dropdown should be closed on outside clicks @default true */
closeOnClickOutside?: boolean;
/** Events that trigger outside clicks */
clickOutsideEvents?: string[];
/** Determines whether focus should be trapped within dropdown @default false */
trapFocus?: boolean;
/** Determines whether dropdown should be closed when `Escape` key is pressed @default true */
closeOnEscape?: boolean;
/** Id base to create accessibility connections */
id?: string;
/** Determines whether dropdown and target elements should have accessible roles @default true */
withRoles?: boolean;
}
export type PopoverFactory = Factory<{
props: PopoverProps;
stylesNames: PopoverStylesNames;
vars: PopoverCssVariables;
}>;
export type ElementProps<
ElementType extends React.ElementType,
PropsToOmit extends string = never,
> = Omit<React.ComponentProps<ElementType>, 'style' | PropsToOmit>;
Native and external types
External types remain defined by React or native dependencies, not manually expanded into guessed props. The installed version's TypeScript definitions are authoritative.
FloatingPosition
FloatingAxesOffsets
TransitionOverride
PopoverWidth
PopoverMiddlewares
OverlayProps
React.ElementType
Omit<React.ComponentProps<ElementType>, 'style' | PropsToOmit>
React.ComponentProps<ElementType>
ElementType
PropsToOmit
ArrowPosition
BasePortalProps
MantineRadius
MantineShadow
FloatingStrategy
StylesApiProps<PopoverFactory>
Factory<{
props: PopoverProps;
stylesNames: PopoverStylesNames;
vars: PopoverCssVariables;
}>
React.ReactNode