Popover

Display popover section relative to given target element
Import

Usage

import { Popover, Text, Button } from '@mantine/core';
function Demo() {
return (
<Popover width={200} position="bottom" withArrow shadow="md">
<Popover.Target>
<Button>Toggle popover</Button>
</Popover.Target>
<Popover.Dropdown>
<Text size="sm">This is uncontrolled popover, it is opened when button is clicked</Text>
</Popover.Dropdown>
</Popover>
);
}

Controlled

You can control Popover state with opened and onChange props:

import { useState } from 'react';
import { Popover, Button } 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:

import { useDisclosure } from '@mantine/hooks';
import { Popover, Text, Button } from '@mantine/core';
function Demo() {
const [opened, { close, open }] = useDisclosure(false);
return (
<Popover width={200} position="bottom" withArrow shadow="md" opened={opened}>
<Popover.Target>
<Button onMouseEnter={open} onMouseLeave={close}>
Hover to see popover
</Button>
</Popover.Target>
<Popover.Dropdown sx={{ pointerEvents: 'none' }}>
<Text size="sm">This popover is shown when user hovers the target element</Text>
</Popover.Dropdown>
</Popover>
);
}

Controlled example with focus events:

import { useState } from 'react';
import { IconX, IconCheck } from '@tabler/icons-react';
import { PasswordInput, Progress, Text, Popover, Box } from '@mantine/core';
function PasswordRequirement({ meets, label }: { meets: boolean; label: string }) {
return (
<Text
color={meets ? 'teal' : 'red'}
sx={{ display: 'flex', alignItems: 'center' }}
mt={7}
size="sm"
>
{meets ? <IconCheck size="0.9rem" /> : <IconX size="0.9rem" />} <Box ml={10}>{label}</Box>
</Text>
);
}
const requirements = [
{ re: /[0-9]/, label: 'Includes number' },
{ re: /[a-z]/, label: 'Includes lowercase letter' },
{ re: /[A-Z]/, label: 'Includes uppercase letter' },
{ re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: 'Includes special symbol' },
];
function getStrength(password: string) {
let multiplier = password.length > 5 ? 0 : 1;
requirements.forEach((requirement) => {
if (!requirement.re.test(password)) {
multiplier += 1;
}
});
return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 10);
}
function Demo() {
const [popoverOpened, setPopoverOpened] = useState(false);
const [value, setValue] = useState('');
const checks = requirements.map((requirement, index) => (
<PasswordRequirement key={index} label={requirement.label} meets={requirement.re.test(value)} />
));
const strength = getStrength(value);
const color = strength === 100 ? 'teal' : strength > 50 ? 'yellow' : 'red';
return (
<Box maw={340} mx="auto">
<Popover opened={popoverOpened} position="bottom" width="target" transitionProps={{ transition: 'pop' }}>
<Popover.Target>
<div
onFocusCapture={() => setPopoverOpened(true)}
onBlurCapture={() => setPopoverOpened(false)}
>
<PasswordInput
withAsterisk
label="Your password"
placeholder="Your password"
value={value}
onChange={(event) => setValue(event.currentTarget.value)}
/>
</div>
</Popover.Target>
<Popover.Dropdown>
<Progress color={color} value={strength} size={5} mb="xs" />
<PasswordRequirement label="Includes at least 6 characters" meets={value.length > 5} />
{checks}
</Popover.Dropdown>
</Popover>
</Box>
);
}

Focus trap

If you need to use any interactive elements within Popover, set trapFocus prop:

import { Popover, Button, TextInput } from '@mantine/core';
function Demo() {
return (
<Popover width={300} trapFocus position="bottom" withArrow shadow="md">
<Popover.Target>
<Button>Toggle popover</Button>
</Popover.Target>
<Popover.Dropdown sx={(theme) => ({ background: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white })}>
<TextInput label="Name" placeholder="Name" size="xs" />
<TextInput label="Email" placeholder="john@doe.com" size="xs" mt="xs" />
</Popover.Dropdown>
</Popover>
);
}

Inline elements

Enable inline middleware to use Popover with inline elements:

Stantler’s magnificent antlers were traded at high prices as works of art. As a result, this Pokémon was hunted close to extinction by those who were after the priceless antlers. , you may catch sight of it having an intense fight with Murkrow over shiny objects.Ho-Oh’s feathers glow in seven colors depending on the angle at which they are struck by light. These feathers are said to bring happiness to the bearers. This Pokémon is said to live at the foot of a rainbow.
import { Popover, Mark, Text } from '@mantine/core';
function Demo() {
return (
<Text>
Stantler’s magnificent antlers were traded at high prices as works of art. As a result, this
Pokémon was hunted close to extinction by those who were after the priceless antlers.{' '}
<Popover middlewares={{ flip: true, shift: true, inline: true }} position="top">
<Popover.Target>
<Mark>When visiting a junkyard</Mark>
</Popover.Target>
<Popover.Dropdown>Inline dropdown</Popover.Dropdown>
</Popover>
, you may catch sight of it having an intense fight with Murkrow over shiny objects.Ho-Oh’s
feathers glow in seven colors depending on the angle at which they are struck by light. These
feathers are said to bring happiness to the bearers. This Pokémon is said to live at the foot
of a rainbow.
</Text>
);
}

Same width

Set width="target" prop to make Popover dropdown take the same width as target element:

import { Popover, Text, Button } from '@mantine/core';
function Demo() {
return (
<Popover width="target" position="bottom" withArrow shadow="md">
<Popover.Target>
<Button w={280}>Toggle popover</Button>
</Popover.Target>
<Popover.Dropdown>
<Text size="sm">
This popover has same width as target, it is useful when you are building input dropdowns
</Text>
</Popover.Dropdown>
</Popover>
);
}

Initial focus

Popover uses FocusTrap component to manage focus. Add data-autofocus attribute to element that should receive initial focus:

<Popover>
<input />
<input data-autofocus />
<input />
</Popover>

Popover.Target children

Popover.Target requires an element or a component as a single child – strings, fragments, numbers and multiple elements/components are not supported and will throw error. Custom components must provide a prop to get root element ref, all Mantine components support ref out of the box.

import { Popover, Button } from '@mantine/core';
function Demo() {
return (
<>
<Popover.Target>
<button>Native button – ok</button>
</Popover.Target>
{/* OK */}
<Popover.Target>
<Button>Mantine component – ok</Button>
</Popover.Target>
{/* String, NOT OK – will throw error */}
<Popover.Target>Raw string</Popover.Target>
{/* Number, NOT OK – will throw error */}
<Popover.Target>{2}</Popover.Target>
{/* Fragment, NOT OK – will throw error */}
<Popover.Target>
<>Fragment, NOT OK, will throw error</>
</Popover.Target>
{/* Multiple nodes, NOT OK – will throw error */}
<Popover.Target>
<div>More that one node</div>
<div>NOT OK, will throw error</div>
</Popover.Target>
</>
);
}

Required ref prop

Custom components that are rendered inside Popover.Target are required to support ref prop:

// Example of code that WILL NOT WORK
import { Popover } from '@mantine/core';
function MyComponent() {
return <div>My component</div>;
}
// This will not work – MyComponent does not support ref
function Demo() {
return (
<Popover>
<Popover.Target>
<MyComponent />
</Popover.Target>
</Popover>
);
}

Use forwardRef function to forward ref to root element:

// Example of code that will work
import { forwardRef } from 'react';
import { Popover } from '@mantine/core';
const MyComponent = forwardRef((props, ref) => (
<div ref={ref} {...props}>
My component
</div>
));
// Works correctly – ref is forwarded
function Demo() {
return (
<Popover>
<Popover.Target>
<MyComponent />
</Popover.Target>
</Popover>
);
}

Accessibility

Popover follows WAI-ARIA recommendations:

  • Dropdown element has role="dialog" and aria-labelledby="target-id" attributes
  • Target element has aria-haspopup="dialog", aria-expanded, aria-controls="dropdown-id" attributes

Supported target elements

Uncontrolled Popover will be accessible only when used with button element or component that renders it (Button, ActionIcon, etc.). Other elements will not support Space and Enter key presses.

Keyboard interactions

KeyDescriptionCondition
EscapeCloses dropdownFocus within dropdown
Space/EnterOpens/closes dropdownFocus on target element

Popover component props

NameTypeDescription
arrowOffset
number
Arrow offset
arrowPosition
"center" | "side"
Arrow position *
arrowRadius
number
Arrow border-radius
arrowSize
number
Arrow size
children *
ReactNode
Popover.Target and Popover.Dropdown components
clickOutsideEvents
string[]
Events that trigger outside clicks
closeOnClickOutside
boolean
Determines whether dropdown should be closed on outside clicks, default to true
closeOnEscape
boolean
Determines whether dropdown should be closed when Escape key is pressed, defaults to true
defaultOpened
boolean
Initial opened state for uncontrolled component
disabled
boolean
If set, popover dropdown will not render
id
string
id base to create accessibility connections
keepMounted
boolean
If set dropdown will not be unmounted from the DOM when it is hidden, display: none styles will be added instead
middlewares
PopoverMiddlewares
Floating ui middlewares to configure position handling
offset
number | FloatingAxesOffsets
Default Y axis or either (main, cross, alignment) X and Y axis space between target element and dropdown
onChange
(opened: boolean) => void
Called with current state when dropdown opens or closes
onClose
() => void
Called when dropdown closes
onOpen
() => void
Called when dropdown opens
onPositionChange
(position: FloatingPosition) => void
Called when dropdown position changes
opened
boolean
Controls dropdown opened state
portalProps
Omit<PortalProps, "children" | "withinPortal">
Props to pass down to the portal when withinPortal is true
position
"bottom" | "left" | "right" | "top" | "bottom-end" | "bottom-start" | "left-end" | "left-start" | "right-end" | "right-start" | "top-end" | "top-start"
Dropdown position relative to target
positionDependencies
any[]
useEffect dependencies to force update dropdown position
radius
number | "xs" | "sm" | "md" | "lg" | "xl"
Key of theme.radius or any valid CSS value to set border-radius, theme.defaultRadius by default
returnFocus
boolean
Determines whether focus should be automatically returned to control when dropdown closes, false by default
shadow
MantineShadow
Key of theme.shadow or any other valid css box-shadow value
transitionProps
Partial<Omit<TransitionProps, "mounted">>
Props added to Transition component that used to animate dropdown presence, use to configure duration and animation type, { duration: 150, transition: 'fade' } by default
trapFocus
boolean
Determines whether focus should be trapped within dropdown, default to false
width
PopoverWidth
Dropdown width, or 'target' to make dropdown width the same as target element
withArrow
boolean
Determines whether component should have an arrow
withRoles
boolean
Determines whether dropdown and target element should have accessible roles, defaults to true
withinPortal
boolean
Determines whether dropdown should be rendered within Portal, defaults to false
zIndex
ZIndex
Dropdown z-index

Popover.Target component props

NameTypeDescription
children *
ReactNode
Target element
popupType
string
Popup accessible type, 'dialog' by default
refProp
string
Key of the prop that should be used to get element ref
shouldOverrideDefaultTargetId
boolean
Determines whether component should override default id of target element, defaults to true

Popover.Dropdown component props

NameTypeDescription
children
ReactNode
Dropdown content

Popover component Styles API

NameStatic selectorDescription
dropdown.mantine-Popover-dropdownDropdown root element
arrow.mantine-Popover-arrowDropdown arrow