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.

Why Handmade Solutions Break

You could query the preference yourself with window.matchMedia('(prefers-reduced-motion: reduce)'). Many developers do, and many ship subtle bugs.

First, you can easily forget the change listener. The initial query runs once when the component mounts. If a user opens your app, then toggles Reduce Motion in system settings because a carousel is making them sick, your component never hears the update. The media query object supports addEventListener, but wiring it up correctly, removing it on unmount, and polyfilling legacy addListener syntax is boilerplate that invites copy-paste errors.

Second, window does not exist during SSR. If your app renders on the server, a naive matchMedia call throws a reference error and kills the render. You end up wrapping the check in typeof window !== 'undefined' guards, guessing at the default, and hoping the client hydrate matches.

Third, repeating that logic in every animated component turns into maintenance debt. One teammate writes the listener one way, another skips it entirely, and a third hard-codes a default that favors motion. A hook centralizes the mess.

useReducedMotion solves all three problems. It returns a simple boolean. It safely no-ops on the server. It attaches and cleans up the listener automatically.

Three Ways to Put It to Work

Once you have the boolean, you need a strategy for acting on it. Here are three patterns that scale from a single component to an entire application.

1. Gate CSS Classes to Avoid Wasted Work

The most direct approach is to prevent heavy JavaScript animation loops from ever starting. Imagine you have a parallax image component that normally listens to scroll events and translates layers inside a requestAnimationFrame loop. Instead of running that logic and then suppressing the visual result, check useReducedMotion first.

If the hook returns true, render the component with a static variant class and skip the useEffect that binds scroll listeners. The browser does less work. Users with motion sensitivity see a fixed image. Everyone else gets the moving layers. Because the animation code never initializes, you save CPU and battery along the way.

2. Feed It Directly into Animation Libraries

If you use a library like Framer Motion, the hook plugs right into your prop definitions. Motion components accept configuration objects for variants, transitions, and gestures. You can use the boolean from useReducedMotion to conditionally set duration: 0 on transitions, or