The usePrefersReducedMotion hook reads the operating system’s prefers-reduced-motion setting so you can tone down or remove motion for users who need it.
Usage
import { usePrefersReducedMotion } from "@servicetitan/anvil2";
function AnimatedComponent() {
const { prefersReducedMotion } = usePrefersReducedMotion();
return (
<div
style={{
transition: prefersReducedMotion ? "none" : "all 0.3s ease",
}}
>
Content with accessible animations
</div>
);
}
Return Value
| Property | Type | Description |
|---|
prefersReducedMotion | boolean | true when the user prefers reduced motion ((prefers-reduced-motion: reduce) matches). |
Examples
Disable transitions
import { usePrefersReducedMotion } from "@servicetitan/anvil2";
function AnimatedComponent() {
const { prefersReducedMotion } = usePrefersReducedMotion();
return (
<div
style={{
transition: prefersReducedMotion ? "none" : "all 0.3s ease",
}}
>
Content with accessible animations
</div>
);
}
Hover motion with CSS
Reduce transform motion when the user prefers reduced motion:
import { usePrefersReducedMotion } from "@servicetitan/anvil2";
import { type ReactNode, useState } from "react";
function HoverCard({ children }: { children: ReactNode }) {
const { prefersReducedMotion } = usePrefersReducedMotion();
const [hovered, setHovered] = useState(false);
const scale = hovered && !prefersReducedMotion ? 1.05 : 1;
return (
<div
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
style={{
transform: `scale(${scale})`,
transition: prefersReducedMotion ? "none" : "transform 0.2s ease",
}}
>
{children}
</div>
);
}
Static alternative to motion-heavy UI
When one part of the UI relies on continuous motion (marquee, auto-rotating carousel, and so on), render a static equivalent when prefersReducedMotion is true—for example a plain list or paginated controls. For libraries such as Framer Motion, install them in your application and thread prefersReducedMotion into their animation props the same way you would for CSS transition.
Technical details
Detection
The implementation uses:
window.matchMedia("(prefers-reduced-motion: reduce)");
and listens for the change event on that MediaQueryList so the returned boolean updates when the user changes the system setting.
SSR
When window is not available (including during SSR), the hook keeps prefersReducedMotion as false until the effect runs in the browser, then it updates to the real preference.
Accessibility guidelines
- Respect the preference: do not override reduced motion with forced animations.
- Preserve meaning: if motion carried information, expose that information without motion.
- Test with the OS setting enabled and confirm layouts and focus order stay usable.
- Reduced motion still allows short, subtle transitions; avoid large parallax or long loops.
How to test
- macOS: System Settings → Accessibility → Display → Reduce motion
- Windows: Settings → Accessibility → Visual effects → Animation effects
- iOS: Settings → Accessibility → Motion → Reduce Motion
- Android: Settings → Accessibility → Remove animations
Browser support
prefers-reduced-motion is supported in current browsers. Where it is unsupported, the media query does not match and the hook reports prefersReducedMotion: false. Last modified on April 14, 2026