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';
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={14} /> : <IconX size={14} />} <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 (
<div style={{ maxWidth: 340, margin: 'auto' }}>
<Popover opened={popoverOpened} position="bottom" width="target" 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} style={{ marginBottom: 10 }} />
<PasswordRequirement label="Includes at least 6 characters" meets={value.length > 5} />
{checks}
</Popover.Dropdown>
</Popover>
</div>
);
}

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

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 sx={{ width: 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
Download more icon variants from https://tabler-icons.io/i/search

Popover component props

NameTypeDescription
arrowOffset
number
Arrow offset in px
arrowSize
number
Arrow size in px
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
exitTransitionDuration
number
Exit transition duration in ms
id
string
id base to create accessibility connections
middlewares
PopoverMiddlewares
Floating ui middlewares to configure position handling
offset
number
Space between target element and dropdown in px
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
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"
Radius from theme.radius or number to set border-radius in px
shadow
MantineShadow
Key of theme.shadow or any other valid css box-shadow value
transition
MantineTransition
One of premade transitions ot transition object
transitionDuration
number
Transition duration in ms
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

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