Back

Ein schlanker Ansatz für Tooltips in React

Ein schlanker Ansatz für Tooltips in React

Sie benötigen einen Tooltip. Vielleicht als Hinweis für einen Icon-Button oder als Kontext für ein gekürztes Label. Bevor Sie zu einem vollständigen UI-Framework oder einer Legacy-Bibliothek wie Tippy.js greifen, überlegen Sie, wie wenig Code Sie tatsächlich benötigen.

Dieser Leitfaden führt durch schlanke Tooltip-Patterns in React – vom einfachsten nativen Ansatz über minimale Custom Hooks bis hin zu Headless-Tooltip-Bibliotheken wie Floating UI. Jeder Schritt erweitert die Funktionalität, ohne Ihr Bundle aufzublähen.

Wichtigste Erkenntnisse

  • Das native title-Attribut bietet Tooltips ohne zusätzliche Kosten, aber ohne Styling und Tastaturunterstützung
  • CSS-only-Tooltips bieten Styling und Focus-States ohne JavaScript, können aber keine Viewport-Kollisionen handhaben
  • Ein minimaler Custom Hook gibt programmatische Kontrolle bei geringer Bundle-Größe
  • Floating UI bietet Kollisionserkennung und Viewport-Awareness in etwa 3kB
  • Priorisieren Sie immer die Barrierefreiheit durch korrekte ARIA-Attribute und platzieren Sie niemals kritische Informationen ausschließlich in Tooltips

Beginnen Sie mit dem nativen Title-Attribut

Das browserinterne title-Attribut ist die JavaScript-freie Baseline:

<button title="Save your changes">💾</button>

Das funktioniert, ist aber eingeschränkt. Der Tooltip erscheint nach einer Verzögerung, Sie können ihn nicht stylen, und er wird nicht bei Tastaturfokus angezeigt. Dennoch kostet es für wirklich einfache Fälle nichts.

CSS-Only React Tooltips

Für gestylte Tooltips ohne JavaScript-Logik übernimmt CSS grundlegende Hover-States:

function IconButton({ label, children }) {
  return (
    <button className="tooltip-trigger" aria-describedby="tooltip-id">
      {children}
      <span className="tooltip" id="tooltip-id" role="tooltip">{label}</span>
    </button>
  )
}
.tooltip-trigger {
  position: relative;
}

.tooltip {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}

.tooltip-trigger:hover .tooltip,
.tooltip-trigger:focus .tooltip {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .tooltip {
    transition: none;
  }
}

Dieses CSS-only-Pattern respektiert prefers-reduced-motion und wird sowohl bei Hover als auch bei Focus ausgelöst. Für die Barrierefreiheit von React-Tooltips bieten role="tooltip" und aria-label am Button den Kontext für Screenreader.

Die Einschränkung? Keine Kollisionserkennung. Wenn der Tooltip nahe am Viewport-Rand sitzt, wird er abgeschnitten.

Ein minimaler Custom Hook

Wenn Sie programmatische Kontrolle ohne Bibliothek benötigen, funktioniert ein kleiner Hook gut:

import { useState, useCallback } from 'react'

function useTooltip() {
  const [isOpen, setIsOpen] = useState(false)

  const triggerProps = {
    onMouseEnter: useCallback(() => setIsOpen(true), []),
    onMouseLeave: useCallback(() => setIsOpen(false), []),
    onFocus: useCallback(() => setIsOpen(true), []),
    onBlur: useCallback(() => setIsOpen(false), []),
  }

  return { isOpen, triggerProps }
}

Verwendung:

function TooltipButton({ hint, children }) {
  const { isOpen, triggerProps } = useTooltip()
  const id = `tooltip-${React.useId()}`

  return (
    <span style={{ position: 'relative', display: 'inline-block' }}>
      <button {...triggerProps} aria-describedby={isOpen ? id : undefined}>
        {children}
      </button>
      {isOpen && (
        <span id={id} role="tooltip" className="tooltip">
          {hint}
        </span>
      )}
    </span>
  )
}

Die aria-describedby-Beziehung verbindet den Button mit seinem Tooltip für assistive Technologien. Das ist wichtig: Tooltips sollten barrierefreie Labels ergänzen, nicht ersetzen. Platzieren Sie niemals kritische Informationen ausschließlich in einem Tooltip.

Headless-Tooltip-Bibliotheken: Floating UI

Wenn Sie eine ordentliche Positionierung benötigen – Kollisionserkennung, Flipping, Shifting – ist Floating UI die moderne Wahl. Es ist der Nachfolger von Popper.js und wiegt etwa 3kB.

Floating UI React Tooltips geben Ihnen Primitives ohne Meinungen zum Styling:

import {
  useFloating,
  offset,
  flip,
  shift,
  useHover,
  useFocus,
  useInteractions,
} from '@floating-ui/react'
import { useState } from 'react'

function Tooltip({ label, children }) {
  const [isOpen, setIsOpen] = useState(false)

  const { refs, floatingStyles, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    middleware: [offset(6), flip(), shift()],
  })

  const hover = useHover(context)
  const focus = useFocus(context)
  const { getReferenceProps, getFloatingProps } = useInteractions([hover, focus])

  return (
    <>
      <span ref={refs.setReference} {...getReferenceProps()}>
        {children}
      </span>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={floatingStyles}
          role="tooltip"
          {...getFloatingProps()}
        >
          {label}
        </div>
      )}
    </>
  )
}

Dies handhabt Viewport-Grenzen automatisch. Die flip-Middleware positioniert neu, wenn der Platz ausgeht, während shift den Tooltip entlang der Achse sichtbar hält.

Für SSR-Sicherheit greifen die Hooks von Floating UI während des Renderns nicht auf window oder document zu. Wenn Sie eine eigene Lösung erstellen, schützen Sie diese Referenzen in useEffect oder useLayoutEffect.

Wann Portale verwendet werden sollten

Wenn das übergeordnete Element Ihres Tooltips overflow: hidden oder komplexe Stacking-Kontexte hat, rendern Sie den Tooltip in einem Portal:

import { createPortal } from 'react-dom'

{isOpen && createPortal(
  <div ref={refs.setFloating} style={floatingStyles} role="tooltip">
    {label}
  </div>,
  document.body
)}

Dies umgeht Clipping-Container. Floating UI handhabt die Positionierung unabhängig davon, wo der Tooltip im DOM gerendert wird.

Warum nicht Tippy.js?

Tippy.js und react-popper haben jahrelang gute Dienste geleistet, sind aber effektiv Legacy. Floating UI bietet die gleiche Positionierungs-Engine mit kleinerem Footprint und besserer React-Integration durch Hooks. Für neue Projekte sind Headless-Tooltip-Bibliotheken wie Floating UI oder Radix UI Tooltip die praktische Wahl.

Wählen Sie Ihren Ansatz

Passen Sie die Komplexität an den Bedarf an:

  • Natives title: Keine Kosten, keine Kontrolle
  • CSS-only: Gestylt, barrierefrei, keine Positionierungslogik
  • Custom Hook: Volle Kontrolle, minimaler Code, manuelle Positionierung
  • Floating UI: Kollisionserkennung, Viewport-Awareness, ~3kB

Fazit

Tooltips müssen nicht schwer sein. Beginnen Sie einfach, fügen Sie Funktionalität hinzu, wenn der Anwendungsfall es erfordert, und stellen Sie die Barrierefreiheit in den Mittelpunkt jeder Implementierung. Das native title-Attribut funktioniert für grundlegende Fälle, CSS-only-Lösungen decken Styling-Anforderungen ab, und wenn Sie Kollisionserkennung benötigen, liefert Floating UI ohne den Ballast von Legacy-Bibliotheken.

FAQs

Verwenden Sie aria-describedby, um das auslösende Element mit dem Tooltip-Inhalt zu verbinden. Fügen Sie role tooltip zum Tooltip-Element selbst hinzu. Halten Sie den Tooltip-Text prägnant und ergänzend. Platzieren Sie niemals essenzielle Informationen ausschließlich in Tooltips, da Nutzer, die auf Tastaturen oder Screenreader angewiesen sind, verzögerte oder nur bei Hover sichtbare Inhalte verpassen könnten.

CSS-only-Tooltips fehlt die Kollisionserkennung. Der Tooltip positioniert sich relativ zu seinem Parent, ohne die Viewport-Grenzen zu prüfen. Verwenden Sie Floating UI mit den flip- und shift-Middlewares, um Tooltips automatisch neu zu positionieren, wenn sie sonst abgeschnitten würden. Alternativ rendern Sie Tooltips in einem Portal, um overflow hidden-Container zu umgehen.

Floating UI ist die empfohlene Wahl für neue Projekte. Es ist der Nachfolger von Popper.js, das Tippy.js antrieb, und bietet eine kleinere Bundle-Größe von etwa 3kB mit besserer React-Integration durch Hooks. Tippy.js funktioniert noch, gilt aber als Legacy und ist schwerer als moderne Alternativen.

Verwenden Sie Portale, wenn Tooltip-Parents overflow hidden, overflow scroll oder komplexe Stacking-Kontexte haben, die Clipping verursachen. Portale rendern den Tooltip direkt in document.body und umgehen so alle Container-Einschränkungen. Floating UI handhabt die Positionierung korrekt, unabhängig davon, wo der Tooltip im DOM-Baum erscheint.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay