Modern websites compete for attention with ever-grander visuals. Parallax heroes stretch deep into the viewport. Carousels scroll indefinitely. Background videos auto-play behind signup forms. These patterns read as polished and engaging on a design portfolio. For a slice of your audience, they are a physical trigger.

People living with vestibular disorders experience nausea, dizziness, or migraines when confronted with large areas of continuous motion. To protect them, every major operating system now includes a "Reduce Motion" accessibility setting. Web browsers expose that preference through the prefers-reduced-motion media query. React developers who want to honor it cleanly can reach for the useReducedMotion hook from @reactuses/core. It reads the OS preference into a boolean, updates live if the user changes their mind, and handles server-side rendering without crashing.

Почему самописные решения ломаются

Вы могли бы самостоятельно запрашивать это предпочтение с помощью window.matchMedia('(prefers-reduced-motion: reduce)'). Многие разработчики так и делают, и многие при этом допускают трудноуловимые ошибки.

Во-первых, можно легко забыть про слушатель изменений. Первичный запрос выполняется один раз при монтировании компонента. Если пользователь откроет ваше приложение, а затем переключит «Уменьшение движения» в системных настройках (потому что карусель вызывает у него тошноту), ваш компонент так и не узнает об обновлении. Объект медиа-запроса поддерживает addEventListener, но правильное подключение, удаление при размонтировании и создание полифиллов для устаревшего синтаксиса addListener — это шаблонный код, который провоцирует ошибки при копировании.

Во-вторых, во время SSR объект window не существует. Если ваше приложение рендерится на сервере, наивный вызов matchMedia вызовет ошибку ссылки и прервет рендеринг. В итоге вам приходится оборачивать проверку в условия typeof window !== 'undefined', гадать с дефолтным значением и надеяться, что гидратация на клиенте пройдет успешно.

В-третьих, повторение этой логики в каждом анимированном компоненте превращается в технический долг. Один коллега напишет слушатель одним способом, другой вовсе его пропустит, а третий жестко пропишет значение по умолчанию, поддерживающее анимацию. Хук позволяет централизовать этот хаос.

useReducedMotion решает все три проблемы. Он возвращает простое логическое значение. Он безопасно ничего не делает на сервере. Он автоматически подключает и очищает слушатель.

Три способа применить это в работе

Как только у вас есть логическое значение, вам нужна стратегия действий. Вот три паттерна, которые масштабируются от одного компонента до всего приложения.

1. Ограничивайте CSS-классы, чтобы избежать лишней работы

Самый прямой подход — предотвратить запуск тяжелых циклов анимации на JavaScript. Представьте, что у вас есть компонент параллакс-изображения, который обычно слушает события прокрутки и перемещает слои внутри цикла requestAnimationFrame. Вместо того чтобы запускать эту логику, а затем подавлять визуальный результат, сначала проверьте useReducedMotion.

Если хук возвращает true, отрендерите компонент с классом статического варианта и пропустите useEffect, который привязывает слушатели прокрутки. Браузер будет выполнять меньше работы. Пользователи с чувствительностью к движению увидят статичное изображение. Все остальные получат движущиеся слои. Поскольку код анимации даже не инициализируется, вы попутно экономите ресурсы процессора и заряд батареи.

2. Передавайте его напрямую в библиотеки анимации

Если вы используете такую библиотеку, как Framer Motion, хук легко встраивается в определения ваших пропсов. Компоненты Motion принимают объекты конфигурации для вариантов, переходов и жестов. Вы можете использовать логическое значение из useReducedMotion, чтобы условно устанавливать duration: 0 для переходов или