useCurrentMotionValue

Reads a motion value reactively, so you can render it in markup or use it in $derived and $effect.

Motion values aren't Svelte state. Changing one updates the elements it's passed to without making Svelte update anything, which is what makes them fast. But it also means that reading one with get() in your markup shows its value once and never updates.

useCurrentMotionValue wraps a motion value in an object with a reactive current property, like Svelte's own Tween and Spring. Read current in your markup, a $derived or an $effect, and it updates whenever the motion value changes.

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

	const x = useMotionValue(0);
	const currentX = useCurrentMotionValue(x);
</script>

<motion.div drag="x" style={{ x }} />
<output>x: {Math.round(currentX.current)}px</output>
<script lang="ts">
	import { motion, useCurrentMotionValue, useMotionValue } from "motion-sv";

	const x = useMotionValue(0);
	const currentX = useCurrentMotionValue(x);
</script>

<div class="stack">
	<motion.div
		style={{ x, width: 80, height: 80, borderRadius: 20, background: "#0cdcf7", cursor: "grab" }}
		drag="x"
		dragConstraints={{ left: -150, right: 150 }}
	/>
	<output>x: {Math.round(currentX.current)}px</output>
</div>

<style>
	.stack {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 1.5rem;
	}

	output {
		font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
		font-variant-numeric: tabular-nums;
	}
</style>

Usage

Import useCurrentMotionValue from motion-sv:

import { useCurrentMotionValue } from "motion-sv";

Pass it a motion value. It returns an object whose current property is the motion value's latest state:

const x = useMotionValue(0);
const currentX = useCurrentMotionValue(x);

currentX.current; // 0

current works like any other Svelte state, so you can derive from it:

<script lang="ts">
	const isFar = $derived(Math.abs(currentX.current) > 100);
</script>

{#if isFar}
	<p>Come back!</p>
{/if}

Animating a number

Combined with useTransform and animate, it makes a counter that animates between numbers:

<script lang="ts">
	import { animate, useCurrentMotionValue, useMotionValue, useTransform } from "motion-sv";

	const count = useMotionValue(0);
	const rounded = useTransform(() => Math.round(count.get()));
	const display = useCurrentMotionValue(rounded);

	$effect(() => {
		const controls = animate(count, 100, { duration: 2 });
		return () => controls.stop();
	});
</script>

<output>{display.current}</output>
<script lang="ts">
	import { animate, useCurrentMotionValue, useMotionValue, useTransform } from "motion-sv";

	const count = useMotionValue(0);
	const rounded = useTransform(() => Math.round(count.get()));
	const display = useCurrentMotionValue(rounded);

	$effect(() => {
		const controls = animate(count, 100, { duration: 2 });
		return () => controls.stop();
	});
</script>

<output>{display.current}</output>

<style>
	output {
		color: #ff0088;
		font-size: 64px;
		font-weight: 700;
		font-variant-numeric: tabular-nums;
	}
</style>

When to use it

Every change to the motion value updates whatever reads current. During an animation, that's once a frame. That's fine for text and logic, but for styles, pass the motion value to style instead. Motion then updates the element directly, without involving Svelte at all:

<script lang="ts">
	const opacity = useMotionValue(1);
	const currentOpacity = useCurrentMotionValue(opacity);
</script>

<!-- Updates the element directly -->
<motion.div style={{ opacity }} />

<!-- Svelte updates this on every frame of the animation -->
<div style:opacity={currentOpacity.current}></div>

To run a side effect when a motion value changes, rather than render it, use useMotionValueEvent.

Subscriptions

useCurrentMotionValue subscribes to the motion value only while current is read somewhere reactive, like your markup, a $derived or an $effect. When nothing reads it any more, for instance because the markup reading it is removed by an {#if}, it unsubscribes.

Reading current outside of a reactive context, like in an event handler, returns the latest value without subscribing, the same as get().

Because it only subscribes when read, useCurrentMotionValue doesn't need to be called during component setup. You can create one anywhere, including in a module shared between components.

API

useCurrentMotionValue<T>(value: MotionValue<T>): { readonly current: T }

value

The motion value to read.

Returns

An object with a reactive current property, the latest state of value.