MotionConfig
Set default options, like a transition or a reduced motion policy, for every motion component inside it.
The MotionConfig component sets configuration options for all the motion components inside it.
<script lang="ts">
import { MotionConfig, motion } from "motion-sv";
</script>
<MotionConfig transition={{ duration: 1 }}>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />
</MotionConfig> Props
transition
A default transition for every motion component inside. A component's own transition prop, or a transition set on an animation target, takes precedence over it.
<MotionConfig transition={{ type: "spring", visualDuration: 0.6, bounce: 0.4 }}>
<motion.div animate={{ x }} />
<motion.div animate={{ x, rotate }} />
<!-- Uses its own transition instead -->
<motion.div animate={{ x }} transition={{ duration: 0.6, ease: "linear" }} />
</MotionConfig> <script lang="ts">
import { MotionConfig, motion } from "motion-sv";
let isOn = $state(false);
const x = $derived(isOn ? 120 : -120);
</script>
<div class="stack">
<MotionConfig transition={{ type: "spring", visualDuration: 0.6, bounce: 0.4 }}>
<motion.div class="box" style={{ background: "#ff3e00" }} animate={{ x }} />
<motion.div class="box" style={{ background: "#9911ff" }} animate={{ x, rotate: isOn ? 180 : 0 }} />
<!-- Its own transition takes precedence -->
<motion.div
class="box"
style={{ background: "#0cdcf7" }}
animate={{ x }}
transition={{ duration: 0.6, ease: "linear" }}
/>
</MotionConfig>
<button onclick={() => (isOn = !isOn)}>Toggle</button>
</div>
<style>
.stack {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.stack :global(.box) {
width: 40px;
height: 40px;
border-radius: 10px;
}
button {
margin-top: 12px;
padding: 8px 20px;
border: none;
border-radius: 8px;
background: #ff3e00;
color: white;
font: inherit;
cursor: pointer;
}
</style> reducedMotion
Default: "never"
A site-wide policy for reduced motion:
"user": respect the user's device setting."always": always reduce motion, which is useful for testing."never": don't reduce motion.
When motion is reduced, transform and layout animations are disabled, and those values change instantly. Other animations, like opacity and backgroundColor, still run.
Each component reads the policy when it mounts, so a change to reducedMotion applies to the components that mount after it.
<MotionConfig reducedMotion="user">
<!-- Your app -->
</MotionConfig> <script lang="ts">
import { MotionConfig, motion } from "motion-sv";
const policies = ["never", "always"] as const;
let isOn = $state(false);
</script>
<div class="stack">
{#each policies as policy (policy)}
<div class="row">
<code>reducedMotion="{policy}"</code>
<div class="track">
<MotionConfig reducedMotion={policy} transition={{ duration: 0.8 }}>
<motion.div
class="box"
initial={false}
animate={{
x: isOn ? 160 : 0,
backgroundColor: isOn ? "#9911ff" : "#ff3e00",
}}
/>
</MotionConfig>
</div>
</div>
{/each}
<button onclick={() => (isOn = !isOn)}>Toggle</button>
</div>
<style>
.stack {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.row {
display: flex;
flex-direction: column;
gap: 6px;
}
code {
font-size: 13px;
}
.track {
width: 200px;
padding: 4px;
border-radius: 12px;
background: rgb(153 17 255 / 0.12);
}
.track :global(.box) {
width: 32px;
height: 32px;
border-radius: 8px;
}
button {
padding: 8px 20px;
border: none;
border-radius: 8px;
background: #ff3e00;
color: white;
font: inherit;
cursor: pointer;
}
</style> skipAnimations
Default: false
If true, every animation inside is skipped, and values are set to their targets instantly. This is useful for end-to-end and visual regression tests.
<MotionConfig skipAnimations={isTesting}>
<!-- Your app -->
</MotionConfig> nonce
If you use a Content Security Policy with a nonce, pass the same nonce to MotionConfig. Motion adds it to the <style> element that AnimatePresence creates for mode="popLayout", so it complies with the policy.
<MotionConfig {nonce}>
<!-- Your app -->
</MotionConfig> inViewOptions
Default inViewOptions for every component inside that uses whileInView, onViewportEnter or onViewportLeave. A component's own inViewOptions replaces them as a whole, rather than being merged with them.
<MotionConfig inViewOptions={{ once: true, amount: 0.5 }}>
<motion.section initial={{ opacity: 0 }} whileInView={{ opacity: 1 }} />
</MotionConfig> Nesting
MotionConfig components can be nested. A nested config inherits every option it doesn't set from its parent, and overrides the ones it does:
<MotionConfig transition={{ duration: 0.5 }} reducedMotion="user">
<!-- Uses the parent's reducedMotion, with a spring -->
<MotionConfig transition={{ type: "spring", bounce: 0.3 }}>
<motion.div animate={{ x: 100 }} />
</MotionConfig>
</MotionConfig> An option that is set replaces the parent's value entirely, so the inner transition above doesn't keep the outer duration. To merge a transition with the parent's instead, set inherit: true. Its own options win:
<MotionConfig transition={{ duration: 0.5, ease: "easeOut" }}>
<!-- Animates for 0.2 seconds, with easeOut -->
<MotionConfig transition={{ inherit: true, duration: 0.2 }}>
<motion.div animate={{ x: 100 }} />
</MotionConfig>
</MotionConfig> useMotionConfig
useMotionConfig() returns a getter for the configuration from the closest MotionConfig, or the defaults when there isn't one. Call it at the top level of a component's <script>, and the getter wherever you need the current value:
<script lang="ts">
import { useMotionConfig } from "motion-sv";
const config = useMotionConfig();
const reducedMotion = $derived(config().reducedMotion);
</script> The getter is reactive, so code that reads it, like the $derived above, updates when the config changes.