Un Enfoque Ligero para Tooltips en React
Necesitas un tooltip. Quizás sea una pista para un botón de icono, o contexto para una etiqueta truncada. Antes de recurrir a un framework UI completo o a una biblioteca heredada como Tippy.js, considera cuán poco código realmente necesitas.
Esta guía recorre patrones ligeros de tooltips en React—desde el enfoque nativo más simple hasta hooks personalizados mínimos y bibliotecas headless de tooltips como Floating UI. Cada paso añade capacidad sin inflar tu bundle.
Puntos Clave
- El atributo nativo
titleproporciona tooltips sin costo pero carece de estilos y soporte de teclado - Los tooltips solo con CSS ofrecen estilos y estados de foco sin JavaScript, pero no pueden manejar colisiones con el viewport
- Un hook personalizado mínimo brinda control programático mientras mantiene el tamaño del bundle pequeño
- Floating UI proporciona detección de colisiones y reconocimiento del viewport en aproximadamente 3kB
- Siempre prioriza la accesibilidad usando atributos ARIA apropiados y nunca coloques información crítica únicamente en tooltips
Comienza con el Atributo Nativo Title
El atributo title integrado del navegador es la línea base sin JavaScript:
<button title="Save your changes">💾</button>
Esto funciona, pero es limitado. El tooltip aparece después de un retraso, no puedes estilizarlo, y no se muestra al enfocar con el teclado. Aún así, para casos verdaderamente simples, no cuesta nada.
Tooltips en React Solo con CSS
Para tooltips estilizados sin lógica JavaScript, CSS maneja estados básicos de hover:
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;
}
}
Este patrón solo con CSS respeta prefers-reduced-motion y se activa tanto con hover como con focus. Para la accesibilidad de tooltips en React, el role="tooltip" y aria-label en el botón proporcionan contexto para lectores de pantalla.
¿La limitación? No hay detección de colisiones. Si el tooltip está cerca del borde del viewport, se recorta.
Un Hook Personalizado Mínimo
Cuando necesitas control programático sin una biblioteca, un hook pequeño funciona bien:
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 }
}
Uso:
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>
)
}
La relación aria-describedby conecta el botón con su tooltip para tecnologías de asistencia. Esto importa: los tooltips deben complementar, no reemplazar, las etiquetas accesibles. Nunca coloques información crítica únicamente en un tooltip.
Discover how at OpenReplay.com.
Bibliotecas Headless de Tooltips: Floating UI
Cuando necesitas posicionamiento adecuado—detección de colisiones, volteo, desplazamiento—Floating UI es la opción moderna. Es el sucesor de Popper.js y pesa alrededor de 3kB.
Los tooltips de Floating UI React te dan primitivas sin opiniones sobre estilos:
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>
)}
</>
)
}
Esto maneja los límites del viewport automáticamente. El middleware flip reposiciona cuando se acaba el espacio, mientras que shift mantiene el tooltip visible a lo largo del eje.
Para seguridad con SSR, los hooks de Floating UI no acceden a window o document durante el renderizado. Si estás construyendo una solución personalizada, protege esas referencias en useEffect o useLayoutEffect.
Cuándo Usar Portals
Si el padre de tu tooltip tiene overflow: hidden o contextos de apilamiento complejos, renderiza el tooltip en un portal:
import { createPortal } from 'react-dom'
{isOpen && createPortal(
<div ref={refs.setFloating} style={floatingStyles} role="tooltip">
{label}
</div>,
document.body
)}
Esto escapa de los contenedores de recorte. Floating UI maneja el posicionamiento independientemente de dónde se renderice el tooltip en el DOM.
¿Por Qué No Tippy.js?
Tippy.js y react-popper sirvieron bien durante años, pero son efectivamente heredados. Floating UI ofrece el mismo motor de posicionamiento con una huella más pequeña y mejor integración con React a través de hooks. Para proyectos nuevos, bibliotecas headless de tooltips como Floating UI o Radix UI Tooltip son la opción práctica.
Eligiendo Tu Enfoque
Ajusta la complejidad a la necesidad:
titlenativo: Costo cero, control cero- Solo CSS: Estilizado, accesible, sin lógica de posicionamiento
- Hook personalizado: Control total, código mínimo, posicionamiento manual
- Floating UI: Detección de colisiones, reconocimiento del viewport, ~3kB
Conclusión
Los tooltips no necesitan ser pesados. Comienza simple, añade capacidad cuando el caso de uso lo demande, y mantén la accesibilidad en el centro de cada implementación. El atributo nativo title funciona para casos básicos, las soluciones solo con CSS manejan necesidades de estilos, y cuando necesitas detección de colisiones, Floating UI entrega sin el peso de bibliotecas heredadas.
Preguntas Frecuentes
Usa aria-describedby para conectar el elemento disparador con el contenido del tooltip. Añade role tooltip al elemento del tooltip mismo. Mantén el texto del tooltip conciso y complementario. Nunca coloques información esencial únicamente en tooltips ya que los usuarios que dependen de teclados o lectores de pantalla pueden perder contenido retrasado o solo con hover.
Los tooltips solo con CSS carecen de detección de colisiones. El tooltip se posiciona relativo a su padre sin verificar los límites del viewport. Usa Floating UI con los middleware flip y shift para reposicionar automáticamente los tooltips cuando de otro modo se recortarían. Alternativamente renderiza tooltips en un portal para escapar de contenedores con overflow hidden.
Floating UI es la opción recomendada para proyectos nuevos. Es el sucesor de Popper.js que impulsaba Tippy.js y ofrece un tamaño de bundle más pequeño alrededor de 3kB con mejor integración con React a través de hooks. Tippy.js todavía funciona pero se considera heredado y tiene más peso que las alternativas modernas.
Usa portals cuando los padres del tooltip tienen overflow hidden, overflow scroll, o contextos de apilamiento complejos que causan recorte. Los portals renderizan el tooltip directamente en document.body escapando de cualquier restricción del contenedor. Floating UI maneja el posicionamiento correctamente independientemente de dónde aparezca el tooltip en el árbol del DOM.
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.