React components library focused on usability, accessibility and developer experience
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
@mantine/core provides comprehensive overlay components for creating modal dialogs, popovers, tooltips, and other floating elements. These components handle complex positioning, focus management, and accessibility requirements.
Full-featured modal dialog component with backdrop, transitions, and accessibility.
interface ModalProps {
/** If true, modal is opened */
opened: boolean;
/** Called when modal should be closed */
onClose: () => void;
/** Modal title */
title?: React.ReactNode;
/** Modal content */
children?: React.ReactNode;
/** Modal size */
size?: MantineSize | string | number;
/** If true, modal will be centered */
centered?: boolean;
/** If true, modal cannot be closed by clicking outside or pressing Escape */
closeOnClickOutside?: boolean;
/** If true, modal cannot be closed by pressing Escape */
closeOnEscape?: boolean;
/** If true, close button will not be rendered */
withCloseButton?: boolean;
/** Modal z-index */
zIndex?: number;
/** Overlay props */
overlayProps?: OverlayProps;
/** Transition props */
transitionProps?: TransitionProps;
/** Modal radius */
radius?: MantineRadius;
/** If true, modal will have full screen on mobile */
fullScreen?: boolean;
/** Modal keep mounted */
keepMounted?: boolean;
}
interface ModalRootProps {
/** Root children */
children: React.ReactNode;
/** If true, modal is opened */
opened: boolean;
/** Called when modal should be closed */
onClose: () => void;
/** Modal z-index */
zIndex?: number;
/** If true, modal cannot be closed by clicking outside */
closeOnClickOutside?: boolean;
/** If true, modal cannot be closed by pressing Escape */
closeOnEscape?: boolean;
/** If true, modal will be centered */
centered?: boolean;
/** Transition props */
transitionProps?: TransitionProps;
/** If true, modal will have full screen on mobile */
fullScreen?: boolean;
}
interface ModalOverlayProps {
/** Overlay background color */
color?: string;
/** Overlay background opacity */
backgroundOpacity?: number;
/** Overlay blur */
blur?: number;
}
interface ModalContentProps {
/** Content children */
children: React.ReactNode;
/** Modal size */
size?: MantineSize | string | number;
/** Modal radius */
radius?: MantineRadius;
}
interface ModalHeaderProps {
/** Header children */
children: React.ReactNode;
}
interface ModalTitleProps {
/** Title content */
children: React.ReactNode;
}
interface ModalCloseButtonProps {
/** Close button aria-label */
'aria-label'?: string;
}
interface ModalBodyProps {
/** Body children */
children: React.ReactNode;
}Usage Example:
import { Modal, Button, TextInput, Group } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function ModalDemo() {
const [opened, { open, close }] = useDisclosure(false);
return (
<>
<Modal opened={opened} onClose={close} title="Authentication">
<TextInput label="Email" placeholder="your@email.com" />
<TextInput label="Password" type="password" mt="md" />
<Group mt="md" justify="flex-end">
<Button onClick={close}>Login</Button>
</Group>
</Modal>
<Button onClick={open}>Open modal</Button>
</>
);
}
// Compound components usage
function CompoundModal() {
const [opened, { open, close }] = useDisclosure(false);
return (
<>
<Modal.Root opened={opened} onClose={close}>
<Modal.Overlay />
<Modal.Content>
<Modal.Header>
<Modal.Title>Modal title</Modal.Title>
<Modal.CloseButton />
</Modal.Header>
<Modal.Body>
Modal content
</Modal.Body>
</Modal.Content>
</Modal.Root>
<Button onClick={open}>Open modal</Button>
</>
);
}Unstyled base modal implementation for custom modal components.
interface ModalBaseProps {
/** If true, modal is opened */
opened: boolean;
/** Called when modal should be closed */
onClose: () => void;
/** Modal content */
children?: React.ReactNode;
/** Modal size */
size?: MantineSize | string | number;
/** If true, modal will be centered */
centered?: boolean;
/** If true, modal cannot be closed by clicking outside */
closeOnClickOutside?: boolean;
/** If true, modal cannot be closed by pressing Escape */
closeOnEscape?: boolean;
/** Modal z-index */
zIndex?: number;
/** Overlay props */
overlayProps?: OverlayProps;
/** Transition props */
transitionProps?: TransitionProps;
/** If true, modal will have full screen on mobile */
fullScreen?: boolean;
}Slide-out drawer component for navigation or content panels.
interface DrawerProps {
/** If true, drawer is opened */
opened: boolean;
/** Called when drawer should be closed */
onClose: () => void;
/** Drawer title */
title?: React.ReactNode;
/** Drawer content */
children?: React.ReactNode;
/** Drawer position */
position?: 'top' | 'bottom' | 'left' | 'right';
/** Drawer size */
size?: MantineSize | string | number;
/** If true, drawer cannot be closed by clicking outside */
closeOnClickOutside?: boolean;
/** If true, drawer cannot be closed by pressing Escape */
closeOnEscape?: boolean;
/** If true, close button will not be rendered */
withCloseButton?: boolean;
/** Drawer z-index */
zIndex?: number;
/** Overlay props */
overlayProps?: OverlayProps;
/** Transition props */
transitionProps?: TransitionProps;
/** If true, drawer will have scroll area */
scrollAreaComponent?: any;
}
interface DrawerRootProps extends Pick<DrawerProps, 'opened' | 'onClose' | 'position' | 'size' | 'closeOnClickOutside' | 'closeOnEscape' | 'zIndex' | 'transitionProps'> {
/** Root children */
children: React.ReactNode;
}
interface DrawerOverlayProps extends ModalOverlayProps {}
interface DrawerContentProps {
/** Content children */
children: React.ReactNode;
}
interface DrawerHeaderProps {
/** Header children */
children: React.ReactNode;
}
interface DrawerTitleProps {
/** Title content */
children: React.ReactNode;
}
interface DrawerCloseButtonProps {
/** Close button aria-label */
'aria-label'?: string;
}
interface DrawerBodyProps {
/** Body children */
children: React.ReactNode;
}Usage Example:
import { Drawer, Button, Group } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function DrawerDemo() {
const [opened, { open, close }] = useDisclosure(false);
return (
<>
<Drawer opened={opened} onClose={close} title="Authentication">
<p>Drawer content...</p>
<Group mt="xl">
<Button variant="outline" onClick={close}>
Cancel
</Button>
<Button onClick={close}>
Save
</Button>
</Group>
</Drawer>
<Button onClick={open}>Open Drawer</Button>
</>
);
}
// Different positions
function DrawerPositions() {
return (
<>
<Drawer position="left" opened={opened} onClose={close}>
Left drawer
</Drawer>
<Drawer position="right" opened={opened} onClose={close}>
Right drawer
</Drawer>
<Drawer position="top" opened={opened} onClose={close}>
Top drawer
</Drawer>
<Drawer position="bottom" opened={opened} onClose={close}>
Bottom drawer
</Drawer>
</>
);
}Floating popover component with positioning and trigger options.
interface PopoverProps {
/** Popover content */
children: React.ReactNode;
/** If true, popover is opened */
opened?: boolean;
/** Default opened state */
defaultOpened?: boolean;
/** Called when popover state changes */
onChange?: (opened: boolean) => void;
/** Popover position relative to target */
position?: FloatingPosition;
/** Popover placement offset */
offset?: number;
/** If true, popover will be closed on outside click */
closeOnClickOutside?: boolean;
/** If true, popover will be closed on escape key press */
closeOnEscape?: boolean;
/** If true, popover will be closed when target element is clicked */
clickOutsideEvents?: string[];
/** Popover width */
width?: number | 'target';
/** If true, popover will have arrow */
withArrow?: boolean;
/** Arrow size */
arrowSize?: number;
/** Arrow position */
arrowPosition?: 'center' | 'side';
/** Arrow offset */
arrowOffset?: number;
/** If true, popover will be disabled */
disabled?: boolean;
/** Popover z-index */
zIndex?: number;
/** Popover radius */
radius?: MantineRadius;
/** Popover shadow */
shadow?: MantineShadow;
/** If true, popover will have focus trap */
trapFocus?: boolean;
/** If true, popover will return focus to trigger when closed */
returnFocus?: boolean;
}
interface PopoverTargetProps {
/** Target element */
children: React.ReactElement;
/** Popover reference type */
refProp?: string;
}
interface PopoverDropdownProps {
/** Dropdown content */
children: React.ReactNode;
}Usage Example:
import { Popover, Button, Text } from '@mantine/core';
function PopoverDemo() {
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</Text>
</Popover.Dropdown>
</Popover>
);
}
// Controlled popover
function ControlledPopover() {
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>
<Text size="sm">Controlled popover content</Text>
</Popover.Dropdown>
</Popover>
);
}Popover that opens on hover with delay support.
interface HoverCardProps extends Omit<PopoverProps, 'opened' | 'onChange'> {
/** Open delay in ms */
openDelay?: number;
/** Close delay in ms */
closeDelay?: number;
/** Called when hover card opens */
onOpen?: () => void;
/** Called when hover card closes */
onClose?: () => void;
}
interface HoverCardTargetProps {
/** Target element */
children: React.ReactElement;
}
interface HoverCardDropdownProps {
/** Dropdown content */
children: React.ReactNode;
}Usage Example:
import { HoverCard, Button, Text, Avatar, Group } from '@mantine/core';
function HoverCardDemo() {
return (
<Group justify="center">
<HoverCard width={280} shadow="md">
<HoverCard.Target>
<Avatar
src="https://images.unsplash.com/photo-1623582854588-d60de57fa33f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80"
radius="xl"
/>
</HoverCard.Target>
<HoverCard.Dropdown>
<div>
<Avatar
src="https://images.unsplash.com/photo-1623582854588-d60de57fa33f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80"
size={94}
radius="md"
/>
<Text fz="sm" tt="uppercase" fw={700} c="dimmed">
Software engineer
</Text>
<Text fz="lg" fw={500} mt={-5}>
Jane Doe
</Text>
<Text fz="sm" c="dimmed">
Jane.Doe@email.com • +47 333 22 11
</Text>
</div>
</HoverCard.Dropdown>
</HoverCard>
</Group>
);
}Simple tooltip component for providing additional information on hover.
interface TooltipProps {
/** Tooltip label */
label: React.ReactNode;
/** Tooltip children (trigger element) */
children: React.ReactNode;
/** Tooltip position */
position?: FloatingPosition;
/** If true, tooltip will be disabled */
disabled?: boolean;
/** Tooltip offset from target */
offset?: number;
/** Tooltip z-index */
zIndex?: number;
/** Tooltip radius */
radius?: MantineRadius;
/** Tooltip color */
color?: MantineColor;
/** If true, tooltip will have arrow */
withArrow?: boolean;
/** Arrow size */
arrowSize?: number;
/** Arrow position */
arrowPosition?: 'center' | 'side';
/** Arrow offset */
arrowOffset?: number;
/** Open delay in ms */
openDelay?: number;
/** Close delay in ms */
closeDelay?: number;
/** If true, tooltip will stay open when hovered */
keepMounted?: boolean;
/** Events that trigger tooltip */
events?: { hover: boolean; focus: boolean; touch: boolean };
}
interface TooltipFloatingProps extends TooltipProps {}
interface TooltipGroupProps {
/** Group children */
children: React.ReactNode;
/** Open delay for all tooltips in group */
openDelay?: number;
/** Close delay for all tooltips in group */
closeDelay?: number;
}Usage Example:
import { Tooltip, Button, Group } from '@mantine/core';
function TooltipDemo() {
return (
<Group>
<Tooltip label="This is tooltip">
<Button variant="outline">Button with tooltip</Button>
</Tooltip>
<Tooltip label="Colored tooltip" color="orange">
<Button variant="outline">Orange tooltip</Button>
</Tooltip>
<Tooltip
label="Tooltip with arrow"
withArrow
position="bottom"
>
<Button variant="outline">With arrow</Button>
</Tooltip>
</Group>
);
}
// Tooltip group
function TooltipGroupDemo() {
return (
<Tooltip.Group openDelay={500} closeDelay={100}>
<Group>
<Tooltip label="Tooltip 1">
<Button variant="outline">Button 1</Button>
</Tooltip>
<Tooltip label="Tooltip 2">
<Button variant="outline">Button 2</Button>
</Tooltip>
<Tooltip label="Tooltip 3">
<Button variant="outline">Button 3</Button>
</Tooltip>
</Group>
</Tooltip.Group>
);
}Context menu component with items, labels, and dividers.
interface MenuProps {
/** Menu content */
children: React.ReactNode;
/** If true, menu is opened */
opened?: boolean;
/** Default opened state */
defaultOpened?: boolean;
/** Called when menu state changes */
onChange?: (opened: boolean) => void;
/** Menu position */
position?: FloatingPosition;
/** Menu offset */
offset?: number;
/** Menu trigger */
trigger?: 'click' | 'hover';
/** Open delay for hover trigger */
openDelay?: number;
/** Close delay for hover trigger */
closeDelay?: number;
/** If true, menu will be closed on item click */
closeOnItemClick?: boolean;
/** If true, menu will be closed on outside click */
closeOnClickOutside?: boolean;
/** If true, menu will be closed on escape */
closeOnEscape?: boolean;
/** Menu width */
width?: number | 'target';
/** If true, menu will have arrow */
withArrow?: boolean;
/** Arrow size */
arrowSize?: number;
/** Arrow position */
arrowPosition?: 'center' | 'side';
/** Arrow offset */
arrowOffset?: number;
/** Menu z-index */
zIndex?: number;
/** Menu radius */
radius?: MantineRadius;
/** Menu shadow */
shadow?: MantineShadow;
/** If true, menu will have focus trap */
trapFocus?: boolean;
/** If true, menu will return focus to trigger */
returnFocus?: boolean;
/** Keyboard events */
loop?: boolean;
}
interface MenuTargetProps {
/** Target element */
children: React.ReactElement;
}
interface MenuDropdownProps {
/** Dropdown content */
children: React.ReactNode;
}
interface MenuItemProps {
/** Item content */
children: React.ReactNode;
/** Item left section */
leftSection?: React.ReactNode;
/** Item right section */
rightSection?: React.ReactNode;
/** Item color when hovered/focused */
color?: MantineColor;
/** If true, item is disabled */
disabled?: boolean;
/** Called when item is clicked */
onClick?: () => void;
}
interface MenuLabelProps {
/** Label content */
children: React.ReactNode;
}
interface MenuDividerProps {
/** Divider label */
label?: React.ReactNode;
}Usage Example:
import { Menu, Button, Text, rem } from '@mantine/core';
function MenuDemo() {
return (
<Menu>
<Menu.Target>
<Button>Toggle menu</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>Application</Menu.Label>
<Menu.Item leftSection="⚙️">
Settings
</Menu.Item>
<Menu.Item leftSection="💬">
Messages
</Menu.Item>
<Menu.Item leftSection="📷">
Gallery
</Menu.Item>
<Menu.Divider />
<Menu.Label>Danger zone</Menu.Label>
<Menu.Item leftSection="🗃️">
Transfer my data
</Menu.Item>
<Menu.Item
color="red"
leftSection="🗑️"
>
Delete my account
</Menu.Item>
</Menu.Dropdown>
</Menu>
);
}Simple dialog component for notifications and confirmations.
interface DialogProps {
/** If true, dialog is opened */
opened: boolean;
/** Called when dialog should be closed */
onClose?: () => void;
/** Dialog content */
children: React.ReactNode;
/** Dialog position */
position?: { top?: number; bottom?: number; left?: number; right?: number };
/** Dialog size */
size?: MantineSize | number;
/** Dialog radius */
radius?: MantineRadius;
/** Dialog shadow */
shadow?: MantineShadow;
/** Dialog z-index */
zIndex?: number;
/** If true, dialog will have close button */
withCloseButton?: boolean;
/** If true, dialog cannot be closed by clicking outside */
closeOnClickOutside?: boolean;
/** If true, dialog cannot be closed by pressing Escape */
closeOnEscape?: boolean;
/** Transition props */
transitionProps?: TransitionProps;
}Usage Example:
import { Dialog, Text, Button, Group } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function DialogDemo() {
const [opened, { toggle, close }] = useDisclosure(false);
return (
<>
<Group justify="center">
<Button onClick={toggle}>Toggle dialog</Button>
</Group>
<Dialog
opened={opened}
withCloseButton
onClose={close}
size="lg"
radius="md"
>
<Text size="sm" mb="xs" fw={500}>
Subscribe to email newsletter
</Text>
<Text size="xs">
Subscribe to our newsletter to stay up to date with the latest news
</Text>
</Dialog>
</>
);
}Generic overlay component for creating backgrounds.
interface OverlayProps {
/** Overlay background color */
color?: string;
/** Overlay opacity */
backgroundOpacity?: number;
/** Overlay blur amount */
blur?: number;
/** Overlay z-index */
zIndex?: number;
/** Overlay radius */
radius?: MantineRadius;
/** If true, overlay will cover entire viewport */
fixed?: boolean;
/** Overlay children */
children?: React.ReactNode;
/** Called when overlay is clicked */
onClick?: () => void;
}Usage Example:
import { Overlay, Button, Box } from '@mantine/core';
import { useState } from 'react';
function OverlayDemo() {
const [visible, setVisible] = useState(false);
return (
<Box pos="relative" h={200}>
{visible && <Overlay color="#000" backgroundOpacity={0.35} />}
<Button onClick={() => setVisible(!visible)}>
Toggle overlay
</Button>
</Box>
);
}Renders children in a different part of the DOM.
interface PortalProps {
/** Portal children */
children: React.ReactNode;
/** Element where portal should be rendered */
target?: HTMLElement | string;
}Usage Example:
import { Portal, Box, Button } from '@mantine/core';
import { useState } from 'react';
function PortalDemo() {
const [opened, setOpened] = useState(false);
return (
<Box>
<Button onClick={() => setOpened(!opened)}>
Toggle portal
</Button>
{opened && (
<Portal>
<Box
style={{
position: 'absolute',
top: 0,
right: 0,
width: 200,
height: 100,
backgroundColor: 'red',
zIndex: 1000,
}}
>
Portal content rendered at document.body
</Box>
</Portal>
)}
</Box>
);
}Fixed positioned element that sticks to viewport edge.
interface AffixProps {
/** Affix position */
position?: { top?: number; bottom?: number; left?: number; right?: number };
/** Affix z-index */
zIndex?: number;
/** If true, affix will be within scrollable container */
withinPortal?: boolean;
/** Affix children */
children: React.ReactNode;
/** Target element for within portal */
portalProps?: PortalProps;
}Usage Example:
import { Affix, Button, Text, Transition } from '@mantine/core';
import { useWindowScroll } from '@mantine/hooks';
function AffixDemo() {
const [scroll, scrollTo] = useWindowScroll();
return (
<>
<Text ta="center">Scroll down to see button</Text>
<Affix position={{ bottom: 20, right: 20 }}>
<Transition transition="slide-up" mounted={scroll.y > 0}>
{(transitionStyles) => (
<Button
leftSection="↑"
style={transitionStyles}
onClick={() => scrollTo({ y: 0 })}
>
Scroll to top
</Button>
)}
</Transition>
</Affix>
</>
);
}Animated indicator that moves between elements.
interface FloatingIndicatorProps {
/** Target element selector or ref */
target: string | HTMLElement;
/** Parent element selector or ref */
parent?: string | HTMLElement;
/** Indicator className */
className?: string;
/** If true, indicator will be disabled */
disabled?: boolean;
}Usage Example:
import { FloatingIndicator, UnstyledButton } from '@mantine/core';
import { useState } from 'react';
function FloatingIndicatorDemo() {
const [rootRef, setRootRef] = useState<HTMLDivElement | null>(null);
const [controlsRefs, setControlsRefs] = useState<Record<string, HTMLButtonElement | null>>({});
const [active, setActive] = useState(0);
const setControlRef = (index: number) => (node: HTMLButtonElement) => {
controlsRefs[index] = node;
setControlsRefs(controlsRefs);
};
const controls = Array(3).fill(0).map((_, index) => (
<UnstyledButton
key={index}
ref={setControlRef(index)}
onClick={() => setActive(index)}
mod={{ active: active === index }}
>
Tab {index + 1}
</UnstyledButton>
));
return (
<div ref={setRootRef}>
<FloatingIndicator
target={controlsRefs[active]}
parent={rootRef}
className="indicator"
/>
{controls}
</div>
);
}Overlay components integrate with the Mantine theme system:
import { MantineProvider } from '@mantine/core';
const theme = {
components: {
Modal: {
defaultProps: {
centered: true,
radius: 'md',
overlayProps: { backgroundOpacity: 0.55, blur: 3 },
},
},
Popover: {
defaultProps: {
position: 'bottom',
withArrow: true,
shadow: 'md',
},
},
Tooltip: {
defaultProps: {
withArrow: true,
color: 'dark',
},
},
},
};Overlay components include comprehensive accessibility support: