mrkeyoor.com_
Tue 03 Nov 23:40 UTC
npmWeb Frontendupdated 03 Nov 2026

@tippyjs/react review

A tooltip wrapper can save you component code and still leave you with a maintenance decision. @tippyjs/react 4.2.6 wraps Tippy.js for React, with built-in and headless rendering. Its repository is archived and read-only. You pass tooltip content through a prop, wrap a reference element, and let the wrapper connect React rendering to a Tippy instance. That makes it worth considering when you maintain an existing Tippy interface. For a new component system, compare the archived wrapper with alternatives before adopting its API. This is a documentation and implementation guide, with unexecuted examples rather than a browser compatibility test.

Verdict

Keep @tippyjs/react on the shortlist for an existing Tippy interface when you can own compatibility testing and future fixes. For new work, evaluate Floating UI or Radix Tooltip before accepting an archived upstream.

Answers from our run

@tippyjs/react or @floating-ui/react: which should you use?

@floating-ui/react: Choose Floating UI when you want positioning and interaction primitives and can build the components yourself. Keep @tippyjs/react on the shortlist for an existing Tippy interface when you can own compatibility testing and future fixes.

When should you not use @tippyjs/react?

Your dependency policy requires an actively maintained upstream repository. This one is archived.

API stability3/5On the stated documentation scale, the versioned README supplies a usable contract for content, controlled visibility, refs, headless rendering, and singleton reuse. The implementation confirms manual triggering in controlled mode and destroys the instance during effect cleanup. That supports understanding the wrapper's behavior, but it does not establish compatibility with current React releases or future dependency changes.
Docs4/5The 4.2.6 README gives separate built-in and headless examples, explains the DOM-ref requirement, shows controlled state and external references, and documents singleton reuse. It also discloses eager content mounting and the HTMLElement requirement for arrows. This is more useful than an installation-only introduction. Several deeper topics lead to external examples, and these instructions have not been validated in a consuming application here.
Maintenance1/5GitHub marks the repository archived and read-only. Under the stated scale, that is a direct adoption constraint for a team expecting upstream fixes. The 4.2.6 release note records a specific singleton headless-content fix, but a historical patch does not establish continuing maintenance. Existing users need their own plan for regressions, dependency updates, or migration.
Ecosystem3/5The versioned README documents both standard and headless React entry points and a singleton hook, while the package metadata declares Tippy.js as a dependency and React and React DOM as peers. Those are useful integration choices for an existing Tippy application. This rating assesses those documented connections only; download counts, adoption, application compatibility, and community response times were not scored.

Use it if

  • You already maintain Tippy-based React screens and need to understand the wrapper before changing them.
  • Your tooltip content is a string or React element tree, and the documented built-in rendering fits your interface.
  • You need Tippy's positioning with a React render function for your own floating markup.
  • Several reference elements should reuse a tooltip through the documented useSingleton hook, and you can test that interaction in your app.
Skip it if

Setup reality

Install the version covered here with npm install @tippyjs/react@4.2.6. The package declares tippy.js ^6.3.1 as a dependency and React plus React DOM >=16.8 as peers. Those ranges do not prove compatibility with every later release. Keep the lockfile and validate your application's resolved versions. This guide did not install the package, execute the snippets, run an audit, or measure a browser bundle.

For built-in rendering, import Tippy from @tippyjs/react. The documented tippy.js/dist/tippy.css import supplies the default styling and is optional if you supply your own. Put a real reference element inside Tippy. When that child is a custom React component, its ref must reach a DOM node. A span wrapper is a documented workaround, but it adds an element you must account for in layout and keyboard navigation.

Choose who owns visibility before wiring events. Controlled mode sets the native trigger to manual and hideOnClick to false. The component warns in development if you also supply trigger, hideOnClick, or showOnCreate. Update your visible state to close it, including in onClickOutside if you want outside clicks to dismiss it. Add and test any keyboard dismissal required by your product rather than assuming that an outside-click callback covers it.

Headless rendering uses the separate @tippyjs/react/headless import. Spread the render callback's attrs onto your element and provide its styling yourself. That callback renders inside a positioned root, so styling the inner element is different from styling the root. A custom positioning arrow must be an HTMLElement; wrap an SVG in a div when using data-popper-arrow.

Hidden content can still mount into the wrapper's container and run component effects. Do not use visibility as your only guard for expensive work. The 4.2.6 release note lists a fix for dynamic singleton headless content. Check that path if your interface shares one tooltip among several triggers.

The repository is archived, so plan who will own fixes before adding this dependency to new work. Start with a real trigger in your application and check focus, dismissal, placement near viewport edges, and unmount behavior. The examples below illustrate the versioned documentation; they do not report those checks as completed. For the primitives approach, see our Floating UI review.

Ratings use a documentation-based scale from 1 to 5: 1 means a documented restriction weighs heavily against adoption, 3 means a usable documented contract with unresolved integration work, and 5 requires strong evidence across the assessed dimension. API stability concerns the published wrapper contract, documentation quality concerns usable guidance, maintenance concerns upstream availability, and ecosystem concerns integration choices. These are editorial judgments, not measured reliability, popularity, or compatibility scores.

Patterns

Wrap a native button show-text-tooltip

import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function SaveHint() {
  return (
    <Tippy content="Save your changes">
      <button type="button">Save</button>
    </Tippy>
  );
}

Documentation-based illustration, unexecuted. The CSS import supplies default styling. Keep an understandable button label even when the tooltip is absent.

Pass React content render-react-content

import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function DraftHint() {
  return (
    <Tippy content={<span>Only <strong>you</strong> can see this draft.</span>}>
      <button type="button">Draft details</button>
    </Tippy>
  );
}

Unexecuted JSX illustration. The versioned README accepts an element tree as content. This example uses explanatory text, not an interactive menu.

Expose the trigger DOM node forward-trigger-ref

import React, { forwardRef } from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

const HelpButton = forwardRef(function HelpButton(props, ref) {
  return <button {...props} ref={ref} type="button" />;
});

export function HelpHint() {
  return (
    <Tippy content="Read the field instructions">
      <HelpButton>Help</HelpButton>
    </Tippy>
  );
}

Unexecuted adaptation of the documented ref pattern. The ref must reach the button DOM node. A component that merely renders a button without forwarding its ref does not meet that contract.

Add a reference around a legacy child wrap-legacy-trigger

import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function LegacyHint({ children }) {
  return (
    <Tippy content="Additional information">
      <span tabIndex={0}>{children}</span>
    </Tippy>
  );
}

Unexecuted wrapper pattern for a child that cannot forward a ref. The span becomes a focus stop. Prefer forwarding the ref for an already focusable control, and check the added element's layout.

Own open state in React control-visibility

import React, { useState } from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function ControlledHint() {
  const [visible, setVisible] = useState(false);
  return (
    <Tippy content="The draft is private" visible={visible}
      onClickOutside={() => setVisible(false)}>
      <button type="button"
        onClick={() => setVisible(value => !value)}
        onKeyDown={event => {
          if (event.key === 'Escape') setVisible(false);
        }}>
        Draft details
      </button>
    </Tippy>
  );
}

Unexecuted state example. visible selects controlled mode, which replaces native triggering and click hiding. The button's Escape handler is application code; it only handles events that reach that button.

Disable the tooltip separately from its trigger disable-tooltip

import React from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function OptionalHint({ hintsEnabled }) {
  return (
    <Tippy content="Export the current view" disabled={!hintsEnabled}>
      <button type="button">Export</button>
    </Tippy>
  );
}

Unexecuted disabled-prop illustration. The implementation calls disable or enable on the Tippy instance. This prop does not set the button's disabled attribute.

Reference an element outside the wrapper use-external-reference

import React, { useRef } from 'react';
import Tippy from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function SeparateHint() {
  const reference = useRef(null);
  return (
    <>
      <button type="button" ref={reference}>Status</button>
      <Tippy content="Changes are saved locally" reference={reference} />
    </>
  );
}

Unexecuted adaptation of the reference example. The README accepts a RefObject or an Element. Keep this example's trigger mounted; it does not establish how arbitrary conditional ref changes should be coordinated.

Supply the inner floating element render-headless-content

import React from 'react';
import Tippy from '@tippyjs/react/headless';

export function CustomHint() {
  return (
    <Tippy render={attrs => (
      <div {...attrs} tabIndex={-1}
        style={{ background: '#222', color: '#fff', padding: 8 }}>
        Project settings
      </div>
    )}>
      <button type="button">Settings help</button>
    </Tippy>
  );
}

Unexecuted headless illustration. Use the headless import and keep the placement attributes. The rendered div is inside the positioned root. Its styling and any additional accessibility behavior remain your responsibility.

Identify a custom HTML arrow position-headless-arrow

import React from 'react';
import Tippy from '@tippyjs/react/headless';

export function ArrowHint() {
  return (
    <Tippy render={attrs => (
      <div {...attrs} tabIndex={-1} className="hint-box">
        Project settings
        <div data-popper-arrow="" className="hint-arrow" />
      </div>
    )}>
      <button type="button">Settings help</button>
    </Tippy>
  );
}

Unexecuted markup illustration. Supply CSS for hint-box and hint-arrow before using it. The README requires an HTMLElement for the arrow; put an SVG inside that div rather than attaching data-popper-arrow to the SVG.

Reuse one tooltip across reference buttons share-tooltip-instance

import React from 'react';
import Tippy, { useSingleton } from '@tippyjs/react';
import 'tippy.js/dist/tippy.css';

export function ToolbarHints() {
  const [source, target] = useSingleton();
  return (
    <>
      <Tippy singleton={source} delay={400} />
      <Tippy singleton={target} content="Save the draft">
        <button type="button">Save</button>
      </Tippy>
      <Tippy singleton={target} content="Export the draft">
        <button type="button">Export</button>
      </Tippy>
    </>
  );
}

Unexecuted adaptation of the documented singleton example. The source supplies the shared tooltip and the targets supply reference elements and content. This built-in rendering example is separate from the headless-content bug named in the 4.2.6 release note.

Alternatives

PackageRegistryPick it when
@floating-ui/reactnpmChoose Floating UI when you want positioning and interaction primitives and can build the components yourself.
@radix-ui/react-tooltipnpmChoose Radix Tooltip when its composed tooltip parts and documented focus, hover, and Escape behavior fit your interface.

More web frontend guides

postcss · react · react-dom · tailwindcss · clsx · htmlparser2 · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.