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

# MotionConfig

> Component that supplies default transitions, reduced-motion behavior, skipAnimations, and coordinate correction to every descendant motion component.

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

---

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

```svelte
<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.div`s 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.

```svelte
<MotionConfig
    transition={{
        type: 'spring',
        stiffness: 260,
        damping: 22
    }}
>
    <motion.div animate={{ x: 100 }} />
    <motion.div animate={{ scale: 1.1 }} />
</MotionConfig>
```

### Per-property defaults

```svelte
<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:

```svelte
<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 |

```svelte
<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.

```svelte
<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.

```svelte
<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](/docs/transform-page-point) and [scaled board example](/examples/transform-page-point).

## Nesting

Configs nest. The nearest ancestor wins per prop — and props from outer configs that aren't overridden still apply.

```svelte
<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={{}}`.

```svelte
<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`](/docs/use-reduced-motion-config):

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

    const reduced = useReducedMotionConfig()
</script>

{#if reduced.current}
    <StaticHero />
{:else}
    <ParallaxHero />
{/if}
```

[`useReducedMotion`](/docs/use-reduced-motion) reads only the OS preference (no `MotionConfig` ancestor required). [`useReducedMotionConfig`](/docs/use-reduced-motion-config) 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](#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 child `transition` replaces the parent's wholesale unless it sets `inherit: true`, which shallow-merges with the parent with child keys winning, matching upstream's `resolveTransition`. A bare `<MotionConfig>` inherits everything; use `transition={{}}` to reset.
- **`skipAnimations` is 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 `skipAnimations` changes 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.
- **`skipAnimations` does 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`](/docs/use-reduced-motion-config) — Reactive read of the resolved policy
- [`useReducedMotion`](/docs/use-reduced-motion) — OS preference only
- [`transformPagePoint`](/docs/transform-page-point) — Coordinate correction contract and limitations
- [WCAG 2.3.3 — Animation from Interactions](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html)

---

Based on [Motion's MotionConfig](https://motion.dev/docs/react-motion-config) API.
