These four libraries offer different approaches to adding tooltip functionality in React applications. @reach/tooltip was part of the Reach UI suite focused on accessibility but is now deprecated. react-popper-tooltip builds on react-popper for flexible positioning logic. react-tippy is a community wrapper around the popular tippy.js engine. react-tooltip is a standalone component library that handles rendering and positioning internally. Each tool balances ease of use, customization, and long-term maintenance differently.
Tooltips seem simple until you need them to work on mobile, handle keyboard navigation, and avoid clipping off the screen. The four packages we are comparing — @reach/tooltip, react-popper-tooltip, react-tippy, and react-tooltip — take different paths to solve these problems. Some rely on established positioning engines, while others bundle everything into one component. Let's look at how they handle real-world engineering challenges.
Before writing code, you must check if the library will be around next year. This affects your technical debt.
@reach/tooltip is part of Reach UI, which is archived.
// @reach/tooltip: Deprecated status
// DO NOT USE in new projects
import { Tooltip } from "@reach/tooltip";
// Reach UI is archived; migrate to Radix UI
react-popper-tooltip relies on react-popper.
react-popper is widely used and stable.// react-popper-tooltip: Stable dependency
import { useTooltipTrigger } from "react-popper-tooltip";
// Built on top of the stable react-popper engine
react-tippy wraps tippy.js.
@tippyjs/react.// react-tippy: Community wrapper
import { Tooltip } from "react-tippy";
// Verify if @tippyjs/react is a better fit for your needs
react-tooltip is a standalone library.
// react-tooltip: Actively maintained
import { Tooltip } from "react-tooltip";
// Regular updates and active issue tracking
How much code do you need to write to get a tooltip on the screen?
@reach/tooltip uses a simple component structure.
// @reach/tooltip: Basic usage
import { Tooltip } from "@reach/tooltip";
<Tooltip label="Edit item">
<button>Edit</button>
</Tooltip>
react-popper-tooltip uses hooks for logic.
// react-popper-tooltip: Hook-based usage
import { useTooltipTrigger } from "react-popper-tooltip";
const { getTriggerProps, getTooltipProps } = useTooltipTrigger();
<button {...getTriggerProps()}>Hover</button>
<div {...getTooltipProps()}>Content</div>
react-tippy uses a component wrapper.
title or content prop.// react-tippy: Component usage
import { Tooltip } from "react-tippy";
<Tooltip title="Delete item">
<button>Delete</button>
</Tooltip>
react-tooltip uses a global or local component.
data-tooltip-id or wrap elements.// react-tooltip: ID-based usage
import { Tooltip } from "react-tooltip";
<button data-tooltip-id="my-tooltip">Info</button>
<Tooltip id="my-tooltip" content="Details" />
Tooltips must flip or move when they hit the edge of the screen. This is where positioning engines matter.
@reach/tooltip uses Popper.js internally.
// @reach/tooltip: Positioning
<Tooltip label="Top content" aria-label="info">
<button>Trigger</button>
</Tooltip>
// Automatically handles edge detection via Popper
react-popper-tooltip exposes Popper options directly.
// react-popper-tooltip: Custom positioning
useTooltipTrigger({
placement: "top",
modifiers: [{ name: "offset", options: { offset: [0, 10] } }]
});
// Full access to Popper.js configuration
react-tippy inherits tippy.js positioning.
top-start, bottom-end.// react-tippy: Placement props
<Tooltip position="top" arrow>
<button>Hover</button>
</Tooltip>
// Uses tippy.js engine for boundary detection
react-tooltip has its own positioning logic.
// react-tooltip: Placement prop
<Tooltip place="top" isOpen={true}>
Content
</Tooltip>
// Internal logic handles screen boundaries
Screen readers and keyboard users need specific ARIA attributes to understand tooltips.
@reach/tooltip was built for accessibility first.
aria-describedby automatically.// @reach/tooltip: ARIA handling
<Tooltip label="Help information">
<button aria-label="Help">?</button>
</Tooltip>
// Automatically links aria-describedby
react-popper-tooltip requires manual ARIA setup.
// react-popper-tooltip: Manual ARIA
<button {...getTriggerProps({ "aria-describedby": id })} />
<div {...getTooltipProps({ id, role: "tooltip" })} />
// Developer manages accessibility attributes
react-tippy supports accessibility props.
aria settings via configuration.// react-tippy: Accessibility props
<Tooltip aria="describedby" interactive>
<button>Info</button>
</Tooltip>
// Configure aria roles via props
react-tooltip includes accessibility features.
role and aria props out of the box.// react-tooltip: ARIA props
<Tooltip role="tooltip" id="acc-tooltip" />
<button aria-describedby="acc-tooltip">Click</button>
// Built-in support for standard ARIA patterns
Can you make the tooltip match your design system without fighting the library?
@reach/tooltip uses data attributes for styling.
[data-reach-tooltip]./* @reach/tooltip: Styling */
[data-reach-tooltip] {
background: #333;
color: #fff;
}
react-popper-tooltip gives you full class control.
/* react-popper-tooltip: Styling */
<div {...getTooltipProps({ className: "my-custom-tooltip" })} />
/* You write all the CSS */
react-tippy allows theme customization.
/* react-tippy: Styling */
<Tooltip theme="light" />
/* Or target .tippy-box in CSS */
react-tooltip supports CSS variables.
/* react-tooltip: Styling */
:root {
--rt-color-white: #222;
}
/* Uses CSS variables for theming */
| Feature | @reach/tooltip | react-popper-tooltip | react-tippy | react-tooltip |
|---|---|---|---|---|
| Status | ❌ Deprecated | ✅ Stable | ⚠️ Community Wrapper | ✅ Active |
| Engine | Popper.js | Popper.js | Tippy.js | Custom/Internal |
| API Style | Component | Hooks | Component | Component/ID |
| A11y | ✅ Automatic | ⚠️ Manual | ✅ Configurable | ✅ Built-in |
| Styling | Data Attributes | Full Control | Themes | CSS Variables |
@reach/tooltip should not be used for new development. The library is archived, and using it introduces immediate technical debt. Migrate existing instances to Radix UI or another active alternative.
react-popper-tooltip is the best choice for design system teams. It gives you the power of Popper.js without forcing a specific look. You build the visuals, it handles the math.
react-tippy works well if you love tippy.js. However, strongly consider @tippyjs/react instead, as it is the official integration and likely to receive faster updates.
react-tooltip is the safest bet for most applications. It balances features, accessibility, and maintenance with minimal setup. Use this if you want to ship quickly without managing positioning logic yourself.
Final Thought: Tooltips are small components with big accessibility responsibilities. Choose the library that lets you maintain compliance without slowing down development — and always check the maintenance status before installing.
Avoid using @reach/tooltip for new projects because the Reach UI library is officially deprecated and no longer maintained. The team recommends migrating to Radix UI for similar accessibility guarantees. Only consider this if you are maintaining a legacy codebase that already depends on Reach UI components and cannot afford a migration yet.
Choose react-popper-tooltip if you need full control over the tooltip's positioning and styling without being locked into a specific design system. It is ideal for teams that want to build a custom design system on top of react-popper and need reliable collision detection and placement logic.
Select react-tippy if you specifically need the feature set of tippy.js and prefer a classic component API. However, note that the official React integration for tippy.js is now @tippyjs/react, so evaluate if that official wrapper better suits your long-term needs before committing to this community wrapper.
Pick react-tooltip if you want a batteries-included solution that works out of the box with minimal configuration. It is well-suited for applications that need a quick implementation with solid accessibility defaults and do not require deep customization of the positioning engine.
When the user's mouse or focus rests on an element, a non-interactive popup is displayed near it.
A couple notes on using tooltips:
Touch Events: Touch events are currently not supported. There's not a lot of research or examples of these types of tooltips on mobile. We have some ideas but need to validate them first before implementing. Please adjust your interfaces on mobile to account for this.
import { Tooltip, useTooltip, TooltipPopup } from "@reach/tooltip";
import { VisuallyHidden } from "@reach/visually-hidden";
import "@reach/tooltip/styles.css";
function Example() {
return (
<Tooltip label="Save">
<button>
<VisuallyHidden>Save</VisuallyHidden>
<span aria-hidden>💾</span>
</button>
</Tooltip>
);
}