logo svelte /motion v0.5.1

Motion values overview

Motion values are reactive primitives that track the state and velocity of animating values. They’re updated and read outside Svelte’s reactive render cycle, so animations can run at full frame rate without triggering component re-renders.

In @humanspeak/svelte-motion, every motion-value-producing hook returns the same shape: a real motion-dom MotionValue (passes isMotionValue, composes with animate(), follows other motion values) augmented with a Svelte-5 reactive .current getter and a .subscribe shim for legacy $store readers.

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

  const x = useSpring(0)
</script>

<div
  style="transform: translateX({x.current}px)"
  onpointerdown={() => x.set(100)}
>
  Spring animated
</div>
<script>
  import { useSpring } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
</script>

<div
  style="transform: translateX({x.current}px)"
  onpointerdown={() => x.set(100)}
>
  Spring animated
</div>

Motion values:

  • Have a .current getter backed by $state for native Svelte 5 reads in templates, $derived, and $effect
  • Can be updated and read outside Svelte’s reactive render cycle
  • Track velocity automatically so springs and transforms stay smooth
  • Can be composed — pass one into another to build animation chains
  • Are SSR-safe — on the server they return static values with no timers
  • Implement Svelte’s Readable store contract via a .subscribe shim, so $x template syntax, svelte/store’s get(), and derived() keep working

Hooks at a glance

HookReturnsRead pattern
useSpringMotionValue<number \| string>spring.current (preferred) or $spring
useMotionValueMotionValue<T>mv.current (preferred) or $mv
useTransformMotionValue<T>transformed.current (preferred) or $transformed
useScroll{ scrollX, scrollY, scrollXProgress, scrollYProgress } of MotionValue<number>scrollY.current (preferred) or $scrollY
useTimeMotionValue<number>time.current (preferred) or $time
useVelocityMotionValue<number>velocity.current (preferred) or $velocity
useMotionTemplateMotionValue<string>template.current (preferred) or $template

For new code, prefer .current everywhere — it tracks under Svelte 5 runes without going through the subscribe path.

Usage

Creating motion values

Use useMotionValue for a value with no spring physics:

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

  const x = useMotionValue(0)
</script>

<div style="transform: translateX({x.current}px)">
  Current value: {x.current}
</div>
<script>
  import { useMotionValue } from '@humanspeak/svelte-motion'

  const x = useMotionValue(0)
</script>

<div style="transform: translateX({x.current}px)">
  Current value: {x.current}
</div>

Use useSpring to animate toward each target with spring physics:

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

  const x = useSpring(0, { stiffness: 300, damping: 30 })
</script>

<button onclick={() => x.set(100)}>Animate</button>
<div style="transform: translateX({x.current}px)">{x.current}</div>
<script>
  import { useSpring } from '@humanspeak/svelte-motion'

  const x = useSpring(0, { stiffness: 300, damping: 30 })
</script>

<button onclick={() => x.set(100)}>Animate</button>
<div style="transform: translateX({x.current}px)">{x.current}</div>

A plain Svelte writable also works for values that don’t need motion-value composition or velocity tracking:

<script>
  import { writable } from 'svelte/store'

  const x = writable(0)
</script>
<script>
  import { writable } from 'svelte/store'

  const x = writable(0)
</script>

Reading values

In templates, $derived, and $effect — use .current:

<div style="transform: translateX({x.current}px)">
  {x.current}
</div>
<div style="transform: translateX({x.current}px)">
  {x.current}
</div>

In imperative code (event handlers, callbacks) — use .get():

<script>
  function logPosition() {
    console.log(x.get())
  }
</script>
<script>
  function logPosition() {
    console.log(x.get())
  }
</script>

The Svelte 4–style $x syntax still works on every motion value via the .subscribe() shim — useful when migrating existing components incrementally.

Setting values

<script>
  x.set(200)   // useSpring: animates with spring physics. useMotionValue: immediate write.
  x.jump(200)  // useSpring only: instantly sets without animation.
</script>
<script>
  x.set(200)   // useSpring: animates with spring physics. useMotionValue: immediate write.
  x.jump(200)  // useSpring only: instantly sets without animation.
</script>

Events

Subscribe to value changes with useMotionValueEvent:

<script>
  import { useMotionValueEvent, useSpring } from '@humanspeak/svelte-motion'
  import { onDestroy } from 'svelte'

  const x = useSpring(0)
  const unsub = useMotionValueEvent(x, 'change', (latest) => {
    console.log('x is now', latest)
  })
  onDestroy(unsub)
</script>
<script>
  import { useMotionValueEvent, useSpring } from '@humanspeak/svelte-motion'
  import { onDestroy } from 'svelte'

  const x = useSpring(0)
  const unsub = useMotionValueEvent(x, 'change', (latest) => {
    console.log('x is now', latest)
  })
  onDestroy(unsub)
</script>

Springs additionally expose motion-dom event types via .on(...):

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

  const x = useSpring(0)

  x.on('animationStart', () => console.log('spring started'))
  x.on('animationComplete', () => console.log('spring settled'))
</script>
<script>
  import { useSpring } from '@humanspeak/svelte-motion'

  const x = useSpring(0)

  x.on('animationStart', () => console.log('spring started'))
  x.on('animationComplete', () => console.log('spring settled'))
</script>

Composition

Motion values become powerful when you chain them. The output of one feeds into the next:

<script>
  import { useSpring, useTransform, useVelocity, useMotionTemplate } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const velocity = useVelocity(x)
  const skew = useTransform(velocity, [-1000, 0, 1000], [-20, 0, 20])
  const filter = useMotionTemplate`drop-shadow(0 0 ${skew}px rgba(0, 0, 0, 0.4))`
</script>

<div
  style="transform: translateX({x.current}px) skewX({skew.current}deg); filter: {filter.current}"
  onpointermove={(e) => x.set(e.clientX)}
>
  Composed chain
</div>
<script>
  import { useSpring, useTransform, useVelocity, useMotionTemplate } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const velocity = useVelocity(x)
  const skew = useTransform(velocity, [-1000, 0, 1000], [-20, 0, 20])
  const filter = useMotionTemplate`drop-shadow(0 0 ${skew}px rgba(0, 0, 0, 0.4))`
</script>

<div
  style="transform: translateX({x.current}px) skewX({skew.current}deg); filter: {filter.current}"
  onpointermove={(e) => x.set(e.clientX)}
>
  Composed chain
</div>

Every step in the chain is a MotionValue, so .current reads reactively without crossing the subscribe boundary.

Chain helpers

  • useTransform — map a motion value’s output range to a new range
  • useSpring — add spring physics to any motion value or readable
  • useVelocity — derive velocity from any motion value
  • useMotionTemplate — build CSS strings from multiple motion values

API

Every motion-value-producing hook in this library returns a value implementing the motion-dom MotionValue interface plus a Svelte 5 reactive surface:

MemberDescription
currentSvelte 5 reactive getter — use in templates / $derived / $effect.
get()Imperative current-value read.
set(value)Update the value. useSpring animates with spring physics; every other hook writes immediately.
jump(value)Set immediately without animation. useSpring only.
on(event, cb)Subscribe to 'change', 'animationStart', 'animationComplete', etc.
getVelocity()Current velocity of the value.
subscribe(run)Svelte readable store contract — powers $mv template syntax and derived(mv, …).
destroy()Tear down the motion value early (normally bound to the surrounding $effect).

See also


Based on Motion’s motion value API.