useReducedMotion

Tracks whether the device's reduced motion setting is on.

useReducedMotion tracks whether the device's reduced motion setting is on.

const reducedMotion = useReducedMotion();

Use it to adapt your UI for people who prefer less motion. For instance, replace x and y animations, which can cause motion sickness, with opacity, stop background videos from autoplaying, or turn off parallax effects.

It follows the setting as it changes, so everything that reads it updates straight away.

<script lang="ts">
	import { motion, useReducedMotion } from "motion-sv";

	let { isOpen }: { isOpen: boolean } = $props();

	const reducedMotion = useReducedMotion();
	const closedX = $derived(reducedMotion.current ? 0 : "-100%");
</script>

<motion.div animate={{ opacity: isOpen ? 1 : 0, x: isOpen ? 0 : closedX }} />
<script lang="ts">
	import { motion, useReducedMotion } from "motion-sv";

	const reducedMotion = useReducedMotion();

	let isOpen = $state(false);

	// With reduced motion, fade the panel in place instead of sliding it
	const closedX = $derived(reducedMotion.current ? 0 : "-100%");
</script>

<div class="frame">
	<motion.div
		class="panel"
		initial={false}
		animate={{ opacity: isOpen ? 1 : 0, x: isOpen ? 0 : closedX }}
		transition={{ duration: 0.4, ease: "easeOut" }}
	>
		Panel
	</motion.div>
	<div class="content">
		<button onclick={() => (isOpen = !isOpen)}>{isOpen ? "Close" : "Open"}</button>
		<p>Reduced motion: {reducedMotion.current ? "on" : "off"}</p>
	</div>
</div>

<style>
	.frame {
		position: relative;
		display: flex;
		width: min(420px, 100%);
		height: 200px;
		border-radius: 16px;
		background: rgb(141 240 204 / 0.12);
		overflow: hidden;
	}

	.frame :global(.panel) {
		position: absolute;
		inset: 0 auto 0 0;
		width: 160px;
		padding: 16px;
		background: #8df0cc;
		color: #0f1115;
		font-weight: 600;
	}

	.content {
		display: flex;
		flex-direction: column;
		align-items: flex-end;
		gap: 8px;
		margin-left: auto;
		padding: 16px;
	}

	button {
		padding: 8px 16px;
		border: none;
		border-radius: 8px;
		background: #8df0cc;
		color: #0f1115;
		font: inherit;
		cursor: pointer;
	}

	p {
		margin: 0;
		font-size: 14px;
	}
</style>

Usage

Import useReducedMotion from motion-sv:

import { useReducedMotion } from "motion-sv";

Call it in any component to find out whether the reduced motion setting is on. Its current property is true when it is:

<script lang="ts">
	import { useReducedMotion } from "motion-sv";

	const reducedMotion = useReducedMotion();
</script>

{#if !reducedMotion.current}
	<video src="/background.mp4" autoplay muted loop></video>
{/if}

current is reactive, so use it in markup, $derived and $effect to change your app's behaviour.

Reduced motion across your app

useReducedMotion is for changing individual animations by hand. To make every motion component in your app respect the setting, wrap it in MotionConfig with reducedMotion="user":

<MotionConfig reducedMotion="user">
	{@render children()}
</MotionConfig>

With reduced motion on, transform and layout animations then jump straight to their final values, while others, like opacity and backgroundColor, still animate.