logo svelte /motion v0.5.1

useMotionTemplate

useMotionTemplate is a tagged template literal that builds an augmented MotionValue<string> from interpolated motion values (or Svelte readables). When any input emits, the template is recomposed.

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

  const blur = useSpring(0)
  const filter = useMotionTemplate`blur(${blur}px)`
</script>

<div style="filter: {filter.current}">Content</div>
<script>
  import { useSpring, useMotionTemplate } from '@humanspeak/svelte-motion'

  const blur = useSpring(0)
  const filter = useMotionTemplate`blur(${blur}px)`
</script>

<div style="filter: {filter.current}">Content</div>

Usage

Pass motion values (or any Svelte readable) as template interpolations. Static strings and current values are interleaved to produce the result.

<script lang="ts">
  import { useSpring, useMotionTemplate } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const y = useSpring(0)
  const shadow = useMotionTemplate`${x}px ${y}px 20px rgba(0, 0, 0, 0.3)`
</script>

<div style="box-shadow: {shadow.current}">
  Dynamic shadow
</div>
<script lang="ts">
  import { useSpring, useMotionTemplate } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const y = useSpring(0)
  const shadow = useMotionTemplate`${x}px ${y}px 20px rgba(0, 0, 0, 0.3)`
</script>

<div style="box-shadow: {shadow.current}">
  Dynamic shadow
</div>

Multiple stores

You can interpolate as many stores as needed:

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

  const hue = useSpring(200)
  const saturation = useSpring(70)
  const lightness = useSpring(50)

  const background = useMotionTemplate`hsl(${hue}, ${saturation}%, ${lightness}%)`
</script>

<div style="background: {background.current}">
  Reactive color
</div>
<script>
  import { useSpring, useMotionTemplate } from '@humanspeak/svelte-motion'

  const hue = useSpring(200)
  const saturation = useSpring(70)
  const lightness = useSpring(50)

  const background = useMotionTemplate`hsl(${hue}, ${saturation}%, ${lightness}%)`
</script>

<div style="background: {background.current}">
  Reactive color
</div>

With useTransform

Combine with useTransform to map values before composing:

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

  const time = useTime()
  // Map elapsed time directly to a 0..1 progress motion value.
  const progress = useTransform(time, [0, 3000], [0, 1], { clamp: false })
  const blur = useTransform(progress, [0, 0.5, 1], [0, 8, 0])
  const brightness = useTransform(progress, [0, 0.5, 1], [1, 1.5, 1])

  const filter = useMotionTemplate`blur(${blur}px) brightness(${brightness})`
</script>

<div style="filter: {filter.current}">
  Animated filter
</div>
<script>
  import { useTime, useTransform, useMotionTemplate } from '@humanspeak/svelte-motion'

  const time = useTime()
  // Map elapsed time directly to a 0..1 progress motion value.
  const progress = useTransform(time, [0, 3000], [0, 1], { clamp: false })
  const blur = useTransform(progress, [0, 0.5, 1], [0, 8, 0])
  const brightness = useTransform(progress, [0, 0.5, 1], [1, 1.5, 1])

  const filter = useMotionTemplate`blur(${blur}px) brightness(${brightness})`
</script>

<div style="filter: {filter.current}">
  Animated filter
</div>

How it works

  1. The tagged template function receives the static string parts and the interpolated inputs (motion values or Svelte readables)
  2. Seeds the result motion value by sampling each input once via sampleSource
  3. Subscribes to every input. The first synchronous emit from each input is skipped (we’ve already seeded) — subsequent emits recompose the template and write to the result via result.set(...)
  4. All subscriptions and the result motion value are torn down when the surrounding $effect scope unmounts

Performance

  • Subscription-driven: Recomposes only when an input emits, no animation frames or polling
  • Single allocation per recompose: The static-parts array is constant; only the composed string is rebuilt
  • SSR-safe: Returns a static augmented motion value composed from the seed snapshots on the server

Common patterns

Dynamic CSS filter

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

  const blur = useSpring(0)
  const brightness = useSpring(1)
  const filter = useMotionTemplate`blur(${blur}px) brightness(${brightness})`
</script>

<img style="filter: {filter.current}" src="photo.jpg" alt="Filtered" />
<script>
  import { useSpring, useMotionTemplate } from '@humanspeak/svelte-motion'

  const blur = useSpring(0)
  const brightness = useSpring(1)
  const filter = useMotionTemplate`blur(${blur}px) brightness(${brightness})`
</script>

<img style="filter: {filter.current}" src="photo.jpg" alt="Filtered" />

Animated gradient

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

  const time = useTime()
  const angle = useTransform(time, [0, 7200], [0, 360], { clamp: false })
  const background = useMotionTemplate`linear-gradient(${angle}deg, #ff6b6b, #4ecdc4)`
</script>

<div style="background: {background.current}">
  Rotating gradient
</div>
<script>
  import { useTime, useTransform, useMotionTemplate } from '@humanspeak/svelte-motion'

  const time = useTime()
  const angle = useTransform(time, [0, 7200], [0, 360], { clamp: false })
  const background = useMotionTemplate`linear-gradient(${angle}deg, #ff6b6b, #4ecdc4)`
</script>

<div style="background: {background.current}">
  Rotating gradient
</div>

Reactive box-shadow

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

  const offsetX = useSpring(0)
  const offsetY = useSpring(4)
  const blurRadius = useSpring(12)
  const shadow = useMotionTemplate`${offsetX}px ${offsetY}px ${blurRadius}px rgba(0, 0, 0, 0.2)`
</script>

<div style="box-shadow: {shadow.current}">
  Hover to lift
</div>
<script>
  import { useSpring, useMotionTemplate } from '@humanspeak/svelte-motion'

  const offsetX = useSpring(0)
  const offsetY = useSpring(4)
  const blurRadius = useSpring(12)
  const shadow = useMotionTemplate`${offsetX}px ${offsetY}px ${blurRadius}px rgba(0, 0, 0, 0.2)`
</script>

<div style="box-shadow: {shadow.current}">
  Hover to lift
</div>

API Reference

Signature

useMotionTemplate(
  strings: TemplateStringsArray,
  ...values: MotionTemplateInput[]
): AugmentedMotionValue<string>

type MotionTemplateInput =
  | AugmentedMotionValue<number | string>
  | Readable<number | string>
useMotionTemplate(
  strings: TemplateStringsArray,
  ...values: MotionTemplateInput[]
): AugmentedMotionValue<string>

type MotionTemplateInput =
  | AugmentedMotionValue<number | string>
  | Readable<number | string>

Parameters

  • strings TemplateStringsArray — Static template string parts (provided automatically by the tagged template syntax).
  • values MotionTemplateInput[] — Motion values or Svelte readables to interpolate into the template.

Returns

An AugmentedMotionValue<string> — a real motion-dom MotionValue containing the composed string, plus:

  • .current — Svelte 5 reactive getter (templates, $derived, $effect).
  • .subscribe(run) — Svelte readable store contract (powers $filter template syntax).
  • All other MotionValue methods from motion-dom (get, on, etc.).

When to use

  • CSS filter chains: Compose blur(), brightness(), saturate() from reactive values
  • Dynamic gradients: Build linear-gradient() or radial-gradient() with animated stops
  • Complex box-shadows: Animate shadow offsets, blur, and spread independently
  • Any CSS value: Anywhere you need to compose a string from multiple reactive sources

For single-value CSS properties, you can use Svelte’s built-in {$store} interpolation directly. useMotionTemplate is most useful when combining multiple reactive values into one CSS string.

See also


Based on Motion’s useMotionTemplate API.