Tooltip
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
Provide a short hint on hover or focus.
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.
| Component prop | Explanation |
|---|---|
Tooltip.label | Visible label or component-specific label content. |
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 }) {
return <M.Tooltip label="A helpful hint"><M.Button>Hover or focus</M.Button></M.Tooltip>;
}
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
Tooltip children
Tooltip requires an element or a component as a single child – strings, fragments, numbers and multiple elements/components are not supported and will throw an error. Custom components must provide a prop to get the root element ref; all Mantine components support ref out of the box.
import { Badge, Tooltip } from '@mantine/core';
function Demo() {
return (
<>
<Tooltip label="OK">
<button>Native button – ok</button>
</Tooltip>
<Tooltip label="OK">
<Badge>Mantine component – ok</Badge>
</Tooltip>
<Tooltip label="Throws">
Raw string, NOT OK – will throw an error
</Tooltip>
{/* Number, NOT OK – will throw an error */}
<Tooltip label="Throws">{2}</Tooltip>
<Tooltip label="Throws">
<>Fragment, NOT OK, will throw an error</>
</Tooltip>
<Tooltip label="Throws">
<div>More than one node</div>
<div>NOT OK, will throw an error</div>
</Tooltip>
</>
);
}
Tooltip target
The target prop is an alternative to children. It accepts a string (selector),
an HTML element or a ref object with an HTML element. Use the target prop when you do
not render the tooltip target as a JSX element.
Example of using the target prop with a string selector:
Hide detached
Use the hideDetached prop to configure how the tooltip behaves when the target
element is removed from the DOM, hidden with display: none, or scrolled out of
the viewport or of a scrollable container that clips it.
By default, hideDetached is enabled – the tooltip is hidden with the target element.
You can change this behavior with hideDetached={false}.
Note that hideDetached has no effect on Tooltip.Floating –
it is positioned relative to the mouse pointer rather than to a target element.
In test environments based on jsdom (Jest, Vitest) every element reports a zero-size rectangle,
so the target is always considered detached and the tooltip is hidden. Set env="test" on
MantineProvider in your test wrapper, as described in the
Jest and Vitest guides, or pass hideDetached={false} –
otherwise hideDetached detection is disabled only in the test environment.
To see the difference, try to scroll the root element of the following demo:
Required ref prop
Custom components that are rendered inside Tooltip are required to support ref prop:
// Example of code that WILL NOT WORK
import { Tooltip } from '@mantine/core';
function MyComponent() {
return <div>My component</div>;
}
// This will not work – MyComponent does not support ref
function Demo() {
return (
<Tooltip label="Does not work">
<MyComponent />
</Tooltip>
);
}
The component must support ref prop:
// Example of code that will work
import { Tooltip } from '@mantine/core';
const MyComponent = ({ ref, ...props }) => (
<div ref={ref} {...props}>
My component
</div>
);
// Works correctly – ref is forwarded
function Demo() {
return (
<Tooltip label="Works fine">
<MyComponent />
</Tooltip>
);
}
Color
Offset
Set the offset prop to a number to change the tooltip position relative to the target element.
This way you can control the tooltip offset on the main axis only.
To control the offset on both axes, pass an object with mainAxis and crossAxis properties:
Arrow
Set the withArrow prop to add an arrow to the tooltip. 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.
If you set arrowPosition to merge, the arrow will form a right triangle merged with the corresponding
corner of the tooltip, and the border radius of that corner will be removed. This mode only works
with *-start and *-end positions. Note that arrowOffset and arrowRadius props are ignored
when arrowPosition is set to merge.
Controlled
Change events
Events that trigger the tooltip can be changed with the events prop; it accepts an object
with the following properties that determine which events will trigger the tooltip:
hover– mouse hover event,trueby defaultfocus– focus/blur events excluding clicks on the target element,falseby defaulttouch– events for touchscreen devices,falseby default
import { Tooltip } from '@mantine/core';
function Demo() {
return (
<Tooltip
label="Tooltip"
events={{ hover: true, focus: true, touch: false }}
>
<button>target</button>
</Tooltip>
);
}
Interactive tooltip
By default, the tooltip closes as soon as the pointer leaves the target element, and the tooltip body ignores pointer events. Set the interactive prop to keep the tooltip open while the pointer travels from the target to the tooltip and rests over its content.
The interactive prop satisfies the hoverable condition of WCAG 2.1 success criterion 1.4.13: Content on Hover or Focus, so the pointer can reach the tooltip body without dismissing it, for example to read or select its content.
Do not put interactive or focusable content (links, buttons, inputs) inside a tooltip. Interactive content should be put in a Popover instead, which is reachable by mouse, keyboard, and touch.
Note that an interactive tooltip receives pointer events, which means that while it is open, it intercepts clicks and hover events of the elements that it overlaps. The tooltip stops receiving pointer events as soon as it starts closing. The interactive prop is not supported by Tooltip.Floating, which always follows the mouse.
Multiline
To enable multiline mode, set the multiline prop to enable line breaks and the w style prop to set the tooltip width:
Inline
Set the inline prop to use Tooltip with inline elements:
Change transition
Tooltip is built with the Transition component; it supports transitionProps props:
import { Button, Tooltip } from '@mantine/core';
function Demo() {
return (
<Tooltip
label="Tooltip with custom transition"
transitionProps={{ transition: 'skew-up', duration: 300 }}
>
<Button>Button with tooltip</Button>
</Tooltip>
);
}
All available premade transitions:
Close and open delay
You can delay tooltip open/close events by setting the openDelay and closeDelay props in ms:
Tooltip delay group
The Tooltip.Group component can be used to sync open and close delays for multiple tooltips:
Floating tooltip
Tooltip.Floating component has the same API as the Tooltip component but the tooltip will follow the mouse:
Accessibility
Tooltip follows the WAI-ARIA tooltip pattern:
- The tooltip body has
role="tooltip"attribute - The target element has
aria-describedbyattribute Tooltip.Floatingis ignored by screen readers- Uncontrolled tooltips are dismissed with
Escapewithout moving the pointer or focus
By default, Tooltip is not triggered by focus events and thus users who use a screen reader
or navigate with the keyboard will not be able to get tooltip content. Set the events prop to enable
focus/blur tooltip events:
import { Button, Tooltip } from '@mantine/core';
// Tooltip will be visible for screen readers
function Demo() {
return (
<Tooltip
label="Tooltip"
events={{ hover: true, focus: true, touch: false }}
>
<Button>Button with tooltip</Button>
</Tooltip>
);
}
WCAG 2.1 success criterion 1.4.13: Content on Hover or Focus
additionally requires content that appears on hover to be dismissable, hoverable and persistent.
Tooltip is persistent out of the box and dismissable if its opened state is not controlled
(Escape closes it without moving the pointer – if you control the opened prop, you are responsible
for handling Escape yourself). Tooltip is not hoverable by default – set the
interactive prop to make the tooltip body hoverable:
import { Button, Tooltip } from '@mantine/core';
// Tooltip stays open while the pointer is over its content
function Demo() {
return (
<Tooltip label="Tooltip" interactive>
<Button>Button with tooltip</Button>
</Tooltip>
);
}
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.
Tooltip(_props)
export interface TooltipProps extends TooltipBaseProps {
/** Called when tooltip position changes */
onPositionChange?: (position: FloatingPosition) => void;
/** Open delay in ms */
openDelay?: number;
/** Close delay in ms @default 0 */
closeDelay?: number;
/** Controlled opened state */
opened?: boolean;
/** Uncontrolled tooltip initial opened state */
defaultOpened?: boolean;
/** Space between target element and tooltip in px @default 5 */
offset?: number | FloatingAxesOffsets;
/** If set, the tooltip has an arrow @default false */
withArrow?: boolean;
/** Arrow size in px @default 4 */
arrowSize?: number;
/** Arrow offset in px @default 5 */
arrowOffset?: number;
/** Arrow `border-radius` in px @default 0 */
arrowRadius?: number;
/** Arrow position relative to the tooltip @default side */
arrowPosition?: ArrowPosition;
/** Props passed down to the `Transition` component that used to animate tooltip presence, use to configure duration and animation type @default { duration: 100, transition: 'fade' } */
transitionProps?: TransitionOverride;
/** Determines which events will be used to show tooltip @default { hover: true, focus: false, touch: false } */
events?: { hover: boolean; focus: boolean; touch: boolean };
/** If set, the tooltip stays open while the pointer is over it @default false */
interactive?: boolean;
/** Must be set if the tooltip target is an inline element */
inline?: boolean;
/** If set, the tooltip is not unmounted from the DOM when hidden, `display: none` styles are applied instead */
keepMounted?: boolean;
/** Changes floating ui [position strategy](https://floating-ui.com/docs/usefloating#strategy) @default 'absolute' */
floatingStrategy?: FloatingStrategy;
/** If set, adjusts text color based on background color for `filled` variant */
autoContrast?: boolean;
/** Selector, ref of an element or element itself that should be used for positioning */
target?: React.RefObject<HTMLElement | null> | HTMLElement | null | string;
/** If set, the tooltip is hidden when the target element is removed from the DOM, hidden with `display: none` or scrolled out of the visible area. Has no effect on `Tooltip.Floating` @default true */
hideDetached?: boolean;
}
export type TooltipFactory = Factory<{
props: TooltipProps;
ref: HTMLDivElement;
stylesNames: TooltipStylesNames;
vars: TooltipCssVariables;
staticComponents: {
Floating: typeof TooltipFloating;
Group: typeof TooltipGroup;
};
}>;
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.
Factory<{
props: TooltipProps;
ref: HTMLDivElement;
stylesNames: TooltipStylesNames;
vars: TooltipCssVariables;
staticComponents: {
Floating: typeof TooltipFloating;
Group: typeof TooltipGroup;
};
}>
TooltipBaseProps
FloatingPosition
FloatingAxesOffsets
ArrowPosition
TransitionOverride
FloatingStrategy
React.RefObject<HTMLElement | null>
HTMLElement
HTMLDivElement
TooltipStylesNames
TooltipCssVariables