useSpring

Creates a motion value that animates to its latest target with a spring.

useSpring creates a motion value that animates to its latest target with a spring animation.

You can set the target yourself with set(), or have it follow another motion value. Here, the ball springs towards motion values that track the pointer:

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

	const spring = { damping: 3, stiffness: 50, restDelta: 0.001 };

	const xPoint = useMotionValue(0);
	const yPoint = useMotionValue(0);
	const x = useSpring(xPoint, spring);
	const y = useSpring(yPoint, spring);
</script>

<motion.div style={{ x, y }} />
<script lang="ts">
	import { frame, motion, useMotionValue, useSpring } from "motion-sv";

	const spring = { damping: 3, stiffness: 50, restDelta: 0.001 };

	const xPoint = useMotionValue(0);
	const yPoint = useMotionValue(0);
	const x = useSpring(xPoint, spring);
	const y = useSpring(yPoint, spring);

	function handlePointerMove({ clientX, clientY, currentTarget }: PointerEvent & { currentTarget: HTMLElement }) {
		frame.read(() => {
			// The ball rests in the middle of the area, so measure the pointer from there
			const { left, top, width, height } = currentTarget.getBoundingClientRect();
			xPoint.set(clientX - left - width / 2);
			yPoint.set(clientY - top - height / 2);
		});
	}
</script>

<div class="area" role="presentation" onpointermove={handlePointerMove}>
	<motion.div class="ball" style={{ x, y }} />
</div>

<style>
	.area {
		display: flex;
		justify-content: center;
		align-items: center;
		width: 100%;
		height: 400px;
	}

	.area :global(.ball) {
		width: 100px;
		height: 100px;
		border-radius: 50%;
		background-color: #ff0088;
	}
</style>

Usage

Import useSpring from motion-sv:

import { useSpring } from "motion-sv";

Like other hooks, call useSpring during component setup, at the top level of the <script>.

Direct control

Create a spring with a number:

const x = useSpring(0);

Now whenever you update it with set(), it animates to its new target with a spring:

x.set(100);

To update it immediately, without a spring, use the jump() method:

x.jump(50);
<script lang="ts">
	import { motion, useSpring } from "motion-sv";

	const x = useSpring(0, { visualDuration: 0.5, bounce: 0.3 });

	const target = () => (x.get() < 100 ? 200 : 0);
</script>

<div class="stack">
	<div class="track">
		<motion.div class="box" style={{ x }} />
	</div>
	<div class="buttons">
		<button onclick={() => x.set(target())}>set()</button>
		<button onclick={() => x.jump(target())}>jump()</button>
	</div>
</div>

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

	.track {
		width: 100%;
		padding: 6px;
		border-radius: 14px;
		background: rgb(255 62 0 / 0.15);
	}

	.track :global(.box) {
		width: 48px;
		height: 48px;
		border-radius: 10px;
		background: #ff3e00;
	}

	.buttons {
		display: flex;
		gap: 0.5rem;
	}

	button {
		padding: 8px 20px;
		border: none;
		border-radius: 8px;
		background: #ff3e00;
		color: white;
		font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
		cursor: pointer;
	}
</style>

It also accepts a value with a unit, like "100vh":

const y = useSpring("100vh");

y.set("50vh");

Track another motion value

A spring can follow the latest state of another motion value:

const x = useMotionValue(0);
const y = useSpring(x);

The source motion value must hold a number, or a string with a unit, like "10px".

This is a handy way to smooth out values that change abruptly, like scroll progress:

const { scrollYProgress } = useScroll();
const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30 });

Transition

Configure the spring with the usual spring transition options, like stiffness, damping and mass, or visualDuration and bounce:

useSpring(0, { stiffness: 300 });
useSpring(0, { visualDuration: 0.4, bounce: 0.25 });

Changing the transition

To change the spring's options over time, pass a getter instead of an object. When the Svelte state it reads changes, the spring's next animation uses the new options:

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

	let isBouncy = $state(false);

	const source = useMotionValue(0);
	const x = useSpring(source, () => ({ stiffness: 300, damping: isBouncy ? 10 : 40 }));
</script>

useFollowValue

useFollowValue works like useSpring, but it can follow its target with other types of transition too, like a tween. Without a type, it uses a spring.

import { useFollowValue } from "motion-sv";

const x = useMotionValue(0);
const tween = useFollowValue(x, { type: "tween", ease: "easeOut" });
<script lang="ts">
	import { clamp, motion, useFollowValue, useMotionValue, useSpring } from "motion-sv";

	const pointer = useMotionValue(0);
	const spring = useSpring(pointer, { stiffness: 300, damping: 20 });
	const tween = useFollowValue(pointer, { type: "tween", ease: "easeOut" });

	let width = $state(0);

	// Centers the 40px dots on the pointer, within the stage
	const follow = (event: PointerEvent) => pointer.set(clamp(0, width - 40, event.offsetX - 20));
</script>

<div class="stage" role="presentation" bind:clientWidth={width} onpointermove={follow}>
	<div class="row">
		<motion.div class="dot pointer" style={{ x: pointer }} />
		<span>pointer</span>
	</div>
	<div class="row">
		<motion.div class="dot" style={{ x: spring, background: "#ff0088" }} />
		<span>useSpring</span>
	</div>
	<div class="row">
		<motion.div class="dot" style={{ x: tween, background: "#9911ff" }} />
		<span>useFollowValue, tween</span>
	</div>
</div>

<style>
	.stage {
		display: flex;
		flex-direction: column;
		gap: 12px;
		width: min(100%, 560px);
		padding: 16px 0;
		border-radius: 20px;
		background: rgb(153 17 255 / 0.08);
		cursor: crosshair;
	}

	.row {
		position: relative;
		height: 40px;
		pointer-events: none;
	}

	span {
		position: absolute;
		top: 50%;
		right: 16px;
		font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
		font-size: 13px;
		opacity: 0.6;
		transform: translateY(-50%);
	}

	.stage :global(.dot) {
		width: 40px;
		height: 40px;
		border-radius: 50%;
	}

	.stage :global(.pointer) {
		box-shadow: inset 0 0 0 3px currentColor;
	}
</style>

Its source can be a number, a string with a unit, or another motion value. Its options are the same as a transition's, without the repeat and lifecycle options, and like useSpring's, they can be a getter.

skipInitialAnimation

Default: false

When true, the first change from the source motion value jumps straight to the new value instead of animating to it. Later changes animate as usual.

This prevents an unwanted animation when the source starts somewhere other than its initial value, like a scroll position that's restored when the page loads:

const { scrollY } = useScroll();
const y = useFollowValue(scrollY, { skipInitialAnimation: true });

API

useSpring(source, config?)
useFollowValue(source, options?)

source

The initial value, as a number or a string with a unit, like "10px", or a motion value to follow.

config / options

The animation's options, as an object, or a getter that returns them. useSpring accepts spring options. useFollowValue accepts any transition options, and skipInitialAnimation.

Returns

A motion value that animates to its latest target.