<!-- Source: https://motion.svelte.page/docs/use-reduced-motion -->

# useReducedMotion

> Reactive Svelte 5 hook for the user's prefers-reduced-motion accessibility preference.

**Source:** [https://motion.svelte.page/docs/use-reduced-motion](https://motion.svelte.page/docs/use-reduced-motion)

---

`useReducedMotion` returns a `$state`-backed `{ current }` object that reflects the user's `prefers-reduced-motion` accessibility setting. `.current` updates live when the media query changes, so components can disable or simplify animations the moment the user toggles the OS preference.

```svelte
<script>
    import { useReducedMotion } from '@humanspeak/svelte-motion'

    const reduced = useReducedMotion()
</script>

<div style:transform={reduced.current ? 'none' : 'rotate(45deg)'}>
    Respects the user's preference
</div>
```

> Diverges from React framer-motion's plain `boolean | null` return for the same reason as `useCycle`: a `$state`-backed value must live on an object so reads inside getters preserve tracking under Svelte 5 runes.

## 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.

> Live example: [/examples/use-reduced-motion](https://motion.svelte.page/examples/use-reduced-motion)

## Usage

Read `reduced.current` directly in templates, `$derived`, and `$effect` — it's reactive via `$state`:

```svelte
<script lang="ts">
    import { useReducedMotion } from '@humanspeak/svelte-motion'

    const reduced = useReducedMotion()
</script>

{#if reduced.current}
    <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:

```svelte
<script lang="ts">
    import { motion, useReducedMotion } from '@humanspeak/svelte-motion'

    const reduced = useReducedMotion()
</script>

<motion.div
    animate={{ x: 100 }}
    transition={reduced.current ? { duration: 0 } : { type: 'spring', stiffness: 200 }}
/>
```

### With variants

When you build variants, fall back to a "no motion" variant for users who opt out:

```svelte
<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.current ? reducedVariants : variants}
    initial="hidden"
    animate="visible"
/>
```

## How it works

- Subscribes to `window.matchMedia('(prefers-reduced-motion: reduce)')` via `$effect`.
- Uses `MediaQueryList` `change` events; falls back to the legacy `addListener` API for Safari &lt; 14.
- Listener is bound to the surrounding reactive scope (the component's lifecycle) — detached automatically on unmount.
- Returns a static `{ current: false }` in SSR or environments without `matchMedia`, so it is safe to call during server rendering.

## API Reference

### Returns

A `ReducedMotionState` object:

- **`current`** `boolean` (getter) — `true` when the user has requested reduced motion, otherwise `false`. Reactive via `$state`.
- **`subscribe(run)`** — Svelte readable store contract. Synchronously emits the current value, then re-emits on every change. Kept for compat with hooks that still consume Svelte readables; prefer `.current` for new code.

## 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.prefersReducedMotion` to `1` in `about:config`.
- **Playwright:** `test.use({ reducedMotion: 'reduce' })` or
  `await page.emulateMedia({ reducedMotion: 'reduce' })` in a test.

## See also

- [useTime](/docs/use-time) - Drive animations from a reactive time store
- [useAnimationFrame](/docs/use-animation-frame) - Frame-by-frame control
- [WCAG 2.3.3 Animation from Interactions](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html)

---

Based on [Motion's useReducedMotion](https://motion.dev/docs/react-use-reduced-motion) API.
