Los sitios web modernos compiten por la atención con elementos visuales cada vez más grandiosos. Los "heroes" con efecto parallax se extienden profundamente en el viewport. Los carruseles se desplazan indefinidamente. Los videos de fondo se reproducen automáticamente detrás de los formularios de registro. Estos patrones se perciben como pulidos y atractivos en un portafolio de diseño. Para una parte de su audiencia, son un desencadenante físico.

Las personas que viven con trastornos vestibulares experimentan náuseas, mareos o migrañas cuando se enfrentan a grandes áreas de movimiento continuo. Para protegerlas, todos los sistemas operativos principales incluyen ahora un ajuste de accesibilidad de "Reducir movimiento". Los navegadores web exponen esa preferencia a través de la media query prefers-reduced-motion. Los desarrolladores de React que quieran respetarla de forma limpia pueden recurrir al hook useReducedMotion de @reactuses/core. Este lee la preferencia del sistema operativo en un booleano, se actualiza en tiempo real si el usuario cambia de opinión y gestiona el renderizado en el lado del servidor sin fallar.

Por qué las soluciones manuales fallan

Podrías consultar la preferencia tú mismo con window.matchMedia('(prefers-reduced-motion: reduce)'). Muchos desarrolladores lo hacen, y muchos introducen errores sutiles.

Primero, es fácil olvidar el escuchador de cambios (change listener). La consulta inicial se ejecuta una vez cuando el componente se monta. Si un usuario abre tu aplicación y luego activa "Reducir movimiento" en los ajustes del sistema porque un carrusel le está provocando malestar, tu componente nunca se enterará de la actualización. El objeto de la media query admite addEventListener, pero configurarlo correctamente, eliminarlo al desmontar (unmount) y aplicar polyfills para la sintaxis heredada de addListener es código repetitivo (boilerplate) que invita a errores de copiar y pegar.

Segundo, window no existe durante el SSR. Si tu aplicación se renderiza en el servidor, una llamada ingenua a matchMedia lanza un error de referencia y detiene el renderizado. Terminas envolviendo la comprobación en guardias de typeof window !== 'undefined', adivinando el valor por defecto y esperando que la hidratación del cliente coincida.

Tercero, repetir esa lógica en cada componente animado se convierte en deuda de mantenimiento. Un compañero escribe el escuchador de una forma, otro lo omite por completo y un tercero codifica un valor por defecto que favorece el movimiento. Un hook centraliza el caos.

useReducedMotion resuelve los tres problemas. Devuelve un simple booleano. Realiza un "no-op" de forma segura en el servidor. Adjunta y limpia el escuchador automáticamente.

Tres formas de ponerlo en práctica

Una vez que tienes el booleano, necesitas una estrategia para actuar en consecuencia. Aquí tienes tres patrones que escalan desde un solo componente hasta una aplicación completa.

1. Controlar las clases CSS para evitar trabajo innecesario

El enfoque más directo es evitar que los pesados bucles de animación de JavaScript lleguen a iniciarse. Imagina que tienes un componente de imagen con parallax que normalmente escucha eventos de scroll y desplaza las capas dentro de un bucle requestAnimationFrame. En lugar de ejecutar esa lógica y luego suprimir el resultado visual, comprueba primero useReducedMotion.

Si el hook devuelve true, renderiza el componente con una clase de variante estática y omite el useEffect que vincula los escuchadores de scroll. El navegador trabaja menos. Los usuarios con sensibilidad al movimiento ven una imagen fija. Todos los demás obtienen las capas en movimiento. Como el código de animación nunca se inicializa, ahorras CPU y batería en el proceso.

2. Pasarlo directamente a las librerías de animación

Si utilizas una librería como Framer Motion, el hook se integra directamente en tus definiciones de props. Los componentes de Motion aceptan objetos de configuración para variantes, transiciones y gestos. Puedes usar el booleano de useReducedMotion para establecer condicionalmente duration: 0 en las transiciones, o