useReducedMotion
useReducedMotion returns a Svelte readable store that reflects the user’s prefers-reduced-motion accessibility setting. The store updates live when the
media query changes, so components can disable or simplify animations the moment
the user toggles the OS preference.
<script>
import { useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
</script>
<div style:transform={$reduced ? 'none' : 'rotate(45deg)'}>
Respects the user's preference
</div><script>
import { useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
</script>
<div style:transform={$reduced ? 'none' : 'rotate(45deg)'}>
Respects the user's preference
</div>Why it matters
Some users disable motion at the OS level because animations cause vestibular
discomfort, distraction, or other accessibility issues. useReducedMotion gives your components a single source of truth so they can opt out of motion
gracefully rather than ignoring the user’s setting.
OS preference: no-preference
Tip: Chrome DevTools → Rendering → emulate prefers-reduced-motion: reduce to test the OS path.
Usage
The hook returns a Readable<boolean> — subscribe with $ like any
Svelte store:
<script lang="ts">
import { useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
</script>
{#if $reduced}
<p>Animations have been disabled to respect your preference.</p>
{:else}
<FancyAnimatedHero />
{/if}<script lang="ts">
import { useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
</script>
{#if $reduced}
<p>Animations have been disabled to respect your preference.</p>
{:else}
<FancyAnimatedHero />
{/if}Skipping motion in motion components
Combine with motion to swap an animated transition for an instant change when
reduced motion is requested:
<script lang="ts">
import { motion, useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
</script>
<motion.div
animate={{ x: 100 }}
transition={$reduced ? { duration: 0 } : { type: 'spring', stiffness: 200 }}
/><script lang="ts">
import { motion, useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
</script>
<motion.div
animate={{ x: 100 }}
transition={$reduced ? { duration: 0 } : { type: 'spring', stiffness: 200 }}
/>With variants
When you build variants, fall back to a “no motion” variant for users who opt out:
<script lang="ts">
import { motion, useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
}
const reducedVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1 }
}
</script>
<motion.div
variants={$reduced ? reducedVariants : variants}
initial="hidden"
animate="visible"
/><script lang="ts">
import { motion, useReducedMotion } from '@humanspeak/svelte-motion'
const reduced = useReducedMotion()
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
}
const reducedVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1 }
}
</script>
<motion.div
variants={$reduced ? reducedVariants : variants}
initial="hidden"
animate="visible"
/>How it works
- Subscribes to
window.matchMedia('(prefers-reduced-motion: reduce)'). - Uses
MediaQueryListchangeevents; falls back to the legacyaddListenerAPI for Safari < 14. - Removes the listener automatically when the last subscriber unsubscribes.
- Returns
falsein SSR or environments withoutmatchMedia, so it is safe to call during server rendering.
API Reference
Returns
Readable<boolean> — true when the user has requested reduced motion,
otherwise false.
Testing the preference
You don’t have to change OS settings to verify your reduced-motion code paths:
- Chrome / Edge DevTools: open DevTools → ⋯ → More tools → Rendering → Emulate CSS media feature
prefers-reduced-motion→reduce. - Firefox: set
ui.prefersReducedMotionto1inabout:config. - Playwright:
test.use({ reducedMotion: 'reduce' })orawait page.emulateMedia({ reducedMotion: 'reduce' })in a test.
See also
- useTime - Drive animations from a reactive time store
- useAnimationFrame - Frame-by-frame control
- WCAG 2.3.3 Animation from Interactions
Based on Motion’s useReducedMotion API.