MotionConfig
<MotionConfig> supplies default transition timing, the reducedMotion policy, skipAnimations, and transformPagePoint coordinate correction to descendant motion.<tag> components. Use it at a route root, an interactive subtree, or a single section.
<script lang="ts">
import { MotionConfig, motion } from '@humanspeak/svelte-motion'
</script>
<MotionConfig transition={{ duration: 0.4, ease: 'easeOut' }}>
<motion.div animate={{ scale: 1.05 }} />
<motion.div animate={{ opacity: 0.5 }} />
</MotionConfig><script lang="ts">
import { MotionConfig, motion } from '@humanspeak/svelte-motion'
</script>
<MotionConfig transition={{ duration: 0.4, ease: 'easeOut' }}>
<motion.div animate={{ scale: 1.05 }} />
<motion.div animate={{ opacity: 0.5 }} />
</MotionConfig>Both motion.divs above pick up { duration: 0.4, ease: 'easeOut' } as their default transition. Any explicit transition prop on a descendant overrides the inherited default.
Default transition
The transition prop accepts any MotionTransition — duration, easing, spring options, per-key overrides, etc.
<MotionConfig
transition={{
type: 'spring',
stiffness: 260,
damping: 22
}}
>
<motion.div animate={{ x: 100 }} />
<motion.div animate={{ scale: 1.1 }} />
</MotionConfig><MotionConfig
transition={{
type: 'spring',
stiffness: 260,
damping: 22
}}
>
<motion.div animate={{ x: 100 }} />
<motion.div animate={{ scale: 1.1 }} />
</MotionConfig>Per-property defaults
<MotionConfig
transition={{
opacity: { duration: 0.2 },
x: { type: 'spring', stiffness: 200 },
default: { duration: 0.4 }
}}
>
<!-- opacity uses 0.2s tween; x uses spring; other keys use the 0.4s default -->
<motion.div animate={{ opacity: 1, x: 0, scale: 1.05 }} />
</MotionConfig><MotionConfig
transition={{
opacity: { duration: 0.2 },
x: { type: 'spring', stiffness: 200 },
default: { duration: 0.4 }
}}
>
<!-- opacity uses 0.2s tween; x uses spring; other keys use the 0.4s default -->
<motion.div animate={{ opacity: 1, x: 0, scale: 1.05 }} />
</MotionConfig>Override at the leaf
Explicit transition on a motion.<tag> always wins:
<MotionConfig transition={{ duration: 0.4 }}>
<motion.div animate={{ x: 100 }} />
<!-- This one ignores the inherited 0.4s and uses 1s instead -->
<motion.div animate={{ x: 100 }} transition={{ duration: 1 }} />
</MotionConfig><MotionConfig transition={{ duration: 0.4 }}>
<motion.div animate={{ x: 100 }} />
<!-- This one ignores the inherited 0.4s and uses 1s instead -->
<motion.div animate={{ x: 100 }} transition={{ duration: 1 }} />
</MotionConfig>reducedMotion policy
reducedMotion controls how transform animations behave for descendants. Three values:
| Value | Behavior |
|---|---|
'never' (default) | Animations run as authored, regardless of OS preference |
'always' | Strip transform keys (x, y, scale, rotate, skew, …). Other props (opacity, color, etc.) still animate |
'user' | Honor the OS-level prefers-reduced-motion: reduce — acts like 'always' for users who opted in, 'never' otherwise |
<MotionConfig reducedMotion="user">
<motion.div
initial={{ x: -200, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ duration: 0.6 }}
>
Always fades in. Translation only runs for users who haven't requested
reduced motion.
</motion.div>
</MotionConfig><MotionConfig reducedMotion="user">
<motion.div
initial={{ x: -200, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ duration: 0.6 }}
>
Always fades in. Translation only runs for users who haven't requested
reduced motion.
</motion.div>
</MotionConfig>'user' is the right default for production sites — it respects the OS accessibility preference without forcing motion off for users who haven’t opted in. Use 'always' for previews / docs surfaces where you want motion disabled regardless of user setting.
skipAnimations
Set skipAnimations to make every animation in the subtree jump directly to its final value instead of tweening. Unlike reducedMotion="always", which strips only transform keys while opacity, color, and other properties continue to animate, skipAnimations disables the tween for every animated property.
<MotionConfig skipAnimations>
<motion.div
initial={{ x: -100, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
/>
<motion.div whileHover={{ scale: 1.1, backgroundColor: '#7c3aed' }} />
</MotionConfig><MotionConfig skipAnimations>
<motion.div
initial={{ x: -100, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
/>
<motion.div whileHover={{ scale: 1.1, backgroundColor: '#7c3aed' }} />
</MotionConfig>This is intended for E2E tests and visual-regression screenshots, where a deterministic settled frame matters more than observing a transition. It covers animate, initial, variants, exit, the whileX gestures, useAnimationControls(), and useAnimate(). Layout/FLIP projection animations and drag momentum are unaffected, matching Framer Motion.
The prop is reactive: toggling it updates already-mounted motion elements, so animation retargets use the current setting without requiring the subtree to remount.
transformPagePoint
Use transformPagePoint to map pointer and measured rectangle coordinates into local units when a parent applies positive CSS scale.
<script lang="ts">
import { MotionConfig, motion, type MotionTransformPoint } from '@humanspeak/svelte-motion'
const scale = 0.5
const correctPoint: MotionTransformPoint = ({ x, y }) => ({
x: x / scale,
y: y / scale
})
</script>
<div style:transform={`scale(${scale})`} style:transform-origin="top left">
<MotionConfig transformPagePoint={correctPoint}>
<motion.div drag dragMomentum={false} />
</MotionConfig>
</div><script lang="ts">
import { MotionConfig, motion, type MotionTransformPoint } from '@humanspeak/svelte-motion'
const scale = 0.5
const correctPoint: MotionTransformPoint = ({ x, y }) => ({
x: x / scale,
y: y / scale
})
</script>
<div style:transform={`scale(${scale})`} style:transform-origin="top left">
<MotionConfig transformPagePoint={correctPoint}>
<motion.div drag dragMomentum={false} />
</MotionConfig>
</div>The callback corrects drag and pan points, offsets, deltas, thresholds, release velocity, ref constraints, and VisualElement measurements. Numeric drag bounds remain authored local values. Gesture input captures the selected callback reference at pointerdown, so replacement affects the next session, while mounted element measurements and refreshed drag geometry read the current config. A stable callback can read mutable state, but retained history is not remapped and can produce large jumps if that state changes while held.
See the full transformPagePoint coordinate contract and scaled board example.
Nesting
Configs nest. The nearest ancestor wins per prop — and props from outer configs that aren’t overridden still apply.
<MotionConfig transition={{ duration: 0.4 }} reducedMotion="user" skipAnimations>
<Outer />
<MotionConfig transition={{ duration: 0.2 }}>
<!-- inherits reducedMotion="user" and skipAnimations, overrides duration -->
<Inner />
</MotionConfig>
</MotionConfig><MotionConfig transition={{ duration: 0.4 }} reducedMotion="user" skipAnimations>
<Outer />
<MotionConfig transition={{ duration: 0.2 }}>
<!-- inherits reducedMotion="user" and skipAnimations, overrides duration -->
<Inner />
</MotionConfig>
</MotionConfig>Resetting inherited defaults
Because configs inherit from their nearest ancestor, a bare <MotionConfig> does not reset anything. To drop an inherited default, pass an explicit empty object such as transition={{}}.
<MotionConfig transition={{ duration: 0.6 }}>
<SlowSection />
<MotionConfig transition={{}}>
<!-- Returns to motion's transition defaults. -->
<DefaultSection />
</MotionConfig>
</MotionConfig><MotionConfig transition={{ duration: 0.6 }}>
<SlowSection />
<MotionConfig transition={{}}>
<!-- Returns to motion's transition defaults. -->
<DefaultSection />
</MotionConfig>
</MotionConfig>transformPagePoint has an additional reset form: omitting it inherits, while explicitly passing transformPagePoint={undefined} clears the inherited callback. An explicit identity function also opts a subtree out while making that intent visible.
Programmatic reads
For per-component decisions that go beyond stripping transforms (e.g. swapping a parallax effect for a static background), read the resolved policy via useReducedMotionConfig:
<script lang="ts">
import { useReducedMotionConfig } from '@humanspeak/svelte-motion'
const reduced = useReducedMotionConfig()
</script>
{#if reduced.current}
<StaticHero />
{:else}
<ParallaxHero />
{/if}<script lang="ts">
import { useReducedMotionConfig } from '@humanspeak/svelte-motion'
const reduced = useReducedMotionConfig()
</script>
{#if reduced.current}
<StaticHero />
{:else}
<ParallaxHero />
{/if}useReducedMotion reads only the OS preference (no MotionConfig ancestor required). useReducedMotionConfig reads the resolved policy from the nearest MotionConfig combined with the OS preference.
Differences from Framer Motion
API coverage
| Prop | Upstream | Svelte Motion | Notes |
|---|---|---|---|
transition | @public | Supported | Sets the default transition for the subtree. |
reducedMotion | @public | Supported | Accepts 'user', 'always', or 'never'. |
skipAnimations | @public | Supported | See the behavioral differences below. |
transformPagePoint | Available on MotionConfig | Supported | Corrects drag, pan, measurements, constraints, and release units inside scaled parents. |
nonce | @public | Not supported | Applies a CSP nonce to inline styles and is relevant only under a strict Content Security Policy. |
isValidProp | @public | Not supported | Decides whether a prop is forwarded to the DOM; upstream uses it when wrapping motion components in a styling library. |
isStatic | Internal; determines whether this is a static context, such as the Framer canvas | Not supported intentionally | A Framer-specific internal with no meaning outside Framer’s editor. |
The two unimplemented @public props, nonce and isValidProp, remain real gaps. isStatic remains a Framer-canvas internal.
Behavioral differences
- Nesting matches upstream. A config inherits from its nearest ancestor and overrides per prop, the same as upstream’s
config = { ...parentConfig, ...config }. A childtransitionreplaces the parent’s wholesale unless it setsinherit: true, which shallow-merges with the parent with child keys winning, matching upstream’sresolveTransition. A bare<MotionConfig>inherits everything; usetransition={{}}to reset. skipAnimationsis more reactive than upstream. In React, the value is latched when an element mounts and never re-read, so toggling it at runtime has no effect on already-mounted elements. Here it is reactive: flipping it applies to already-mounted elements, and the next animation they run uses the current setting without a remount.useAnimate()reads the current value on every call for the same reason.- In-flight animations are not interrupted (same as upstream). Flipping
skipAnimationschanges what animations started after the flip do; an animation already running finishes its tween. For a deterministic screenshot, set it before the animation starts rather than mid-flight. skipAnimationsdoes not affect layout/FLIP projection animations or drag momentum (same as upstream). Those do not route through the value-level animation path the switch controls.- Coordinate input is session-captured. Pointer samples keep the callback selected at pointerdown; replacing the callback affects the next gesture. VisualElement/config measurements remain live, and a stable callback reads its latest closed-over values without remapping old gesture history, matching the verified Motion behavior.
See also
useReducedMotionConfig— Reactive read of the resolved policyuseReducedMotion— OS preference onlytransformPagePoint— Coordinate correction contract and limitations- WCAG 2.3.3 — Animation from Interactions
Based on Motion’s MotionConfig API.