Vue 3SvelteSolid.jsJune 2026 · 9 min read

scrollAnimate in Vue 3,
Svelte, and Solid.js

svg-scroll-draw v2.3 ships first-class wrappers for all three frameworks. This guide covers every API — composables, actions, hooks, component wrappers — with copy-paste examples for real-world patterns.

1. Install

One package, all frameworks. Install once and import from the framework-specific subpath.

terminal
npm install svg-scroll-draw
# or
pnpm add svg-scroll-draw

Vue, Svelte, and Solid are all peer dependencies — they're already in your project. The wrappers tree-shake cleanly; only the code you import gets bundled.

2. Vue 3

All v2 APIs ship as composables that return a ref you bind with :ref. There are also ready-made component wrappers if you prefer that style.

scrollAnimate — fade + slide on scroll

Hero.vue
<script setup>
import { useScrollAnimate } from 'svg-scroll-draw/vue';

const el = useScrollAnimate({
  props: {
    opacity:   [0, 1],
    transform: ['translateY(40px)', 'translateY(0)'],
  },
  easing: 'ease-out',
  once:   true,
});
</script>

<template>
  <div :ref="el">
    This fades and slides in as it scrolls into view.
  </div>
</template>

scrollText — split + stagger animate

Headline.vue
<script setup>
import { useScrollText } from 'svg-scroll-draw/vue';

const headline = useScrollText({
  split:   'words',
  stagger: 0.05,
  from:    { opacity: 0, y: 24 },
  once:    true,
});

const subline = useScrollText({
  split:   'chars',
  stagger: 0.015,
  from:    { opacity: 0 },
  once:    true,
});
</script>

<template>
  <h1 :ref="headline">Ship faster. Zero GSAP.</h1>
  <p  :ref="subline">Every character trickles in on scroll.</p>
</template>

scrollCounter — animated numbers

Stats.vue
<script setup>
import { useScrollCounter } from 'svg-scroll-draw/vue';

const users = useScrollCounter({
  to:     50_000,
  format: n => Math.round(n).toLocaleString() + '+',
  easing: 'ease-out',
  once:   true,
});
</script>

<template>
  <!-- Renders a <span> that counts 0 → 50,000+ on scroll -->
  <span :ref="users" />

Component wrappers

Prefer components over composables? All four v2 APIs have component wrappers too.

Page.vue
<script setup>
import { ScrollAnimate, ScrollText, ScrollCounter } from 'svg-scroll-draw/vue';
</script>

<template>
  <!-- Wraps children in a <div> and animates -->
  <ScrollAnimate :options="{ props: { opacity: [0,1] }, easing: 'ease-out', once: true }">
    <MyCard />
  </ScrollAnimate>

  <!-- Renders a <p> tag by default (change with tag="h2") -->
  <ScrollText :options="{ split: 'words', stagger: 0.05 }" tag="h2">
    Animate on scroll.
  </ScrollText>

  <!-- Renders a <span> counter -->
  <ScrollCounter
    :to="1250000"
    :format="n => '$' + Math.round(n).toLocaleString()"
    easing="ease-out"
    :once="true"
  />
</template>
Nuxt users: import from svg-scroll-draw/nuxt instead — it re-exports everything and includes a plugin factory for global component registration.

3. Svelte

All v2 APIs are Svelte use: actions. The matching create* helpers give you access to the live instance for replay, pause, and seek.

scrollAnimate action

Hero.svelte
<script>
  import { scrollAnimate } from 'svg-scroll-draw/svelte';

  const opts = {
    props: {
      opacity:   [0, 1],
      transform: ['translateY(40px)', 'translateY(0)'],
    },
    easing: 'ease-out',
    once:   true,
  };
</script>

<div use:scrollAnimate={opts}>
  Fades and slides in on scroll.
</div>

scrollTextAction — split text

Headline.svelte
<script>
  import { scrollTextAction } from 'svg-scroll-draw/svelte';
</script>

<h1 use:scrollTextAction={{ split: 'words', stagger: 0.05, once: true }}>
  Ship faster. Zero GSAP.
</h1>

<p use:scrollTextAction={{ split: 'chars', stagger: 0.015, from: { opacity: 0 }, once: true }}>
  Every character trickles in on scroll.
</p>

createScrollAnimate — with instance control

HeroWithReplay.svelte
<script>
  import { createScrollAnimate } from 'svg-scroll-draw/svelte';

  const { action, getInstance } = createScrollAnimate({
    props: { opacity: [0, 1], transform: ['translateY(40px)', 'translateY(0)'] },
    easing: 'ease-out',
    once:   true,
  });
</script>

<div use:action>...</div>
<button on:click={() => getInstance()?.replay()}>↺ Replay</button>

The same create* pattern is available for all v2 actions:createScrollCounter,createScrollVideo,createScrollText.

4. Solid.js

All v2 APIs are SolidJS hooks that return a ref setter. The create* variants return { ref, getInstance } for imperative control.

useScrollAnimate

Hero.tsx
import { useScrollAnimate } from 'svg-scroll-draw/solid';

function Hero() {
  const ref = useScrollAnimate({
    props: {
      opacity:   [0, 1],
      transform: ['translateY(40px)', 'translateY(0)'],
    },
    easing: 'ease-out',
    once:   true,
  });

  return <div ref={ref}>Fades and slides in on scroll.</div>;
}

useScrollText

Headline.tsx
import { useScrollText } from 'svg-scroll-draw/solid';

function Headline() {
  const headRef = useScrollText({
    split:   'words',
    stagger: 0.05,
    from:    { opacity: 0, y: 24 },
    once:    true,
  });

  return <h1 ref={headRef}>Ship faster. Zero GSAP.</h1>;
}

createScrollAnimate — with instance access

HeroWithReplay.tsx
import { createScrollAnimate } from 'svg-scroll-draw/solid';

function HeroSection() {
  const { ref, getInstance } = createScrollAnimate({
    props: { opacity: [0, 1], transform: ['translateY(40px)', 'translateY(0)'] },
    easing: 'ease-out',
    once:   true,
  });

  return (
    <>
      <div ref={ref}>...</div>
      <button onClick={() => getInstance()?.replay()}>↺ Replay</button>
    </>
  );
}

5. Real-world patterns

Staggered card grid (Vue)

Use v-for with independent composable calls — one instance per card, each with a trigger offset for a natural cascade:

FeatureGrid.vue
<script setup>
import { useScrollAnimate } from 'svg-scroll-draw/vue';
import { ref as vueRef, onMounted } from 'vue';
import { scrollAnimate } from 'svg-scroll-draw';

const cardRefs = vueRef([]);

onMounted(() => {
  cardRefs.value.forEach((el, i) => {
    scrollAnimate(el, {
      props: {
        opacity:   [0, 1],
        transform: ['translateY(32px)', 'translateY(0)'],
      },
      trigger: {
        start: `top ${90 - i * 4}%`,
        end:   `top ${55 - i * 4}%`,
      },
      easing: 'ease-out',
      once:   true,
    });
  });
});
</script>

<template>
  <div class="grid">
    <div v-for="(card, i) in cards" :key="i" :ref="el => cardRefs[i] = el">
      {{ card.title }}
    </div>
  </div>
</template>

Marketing headline (Svelte)

HeroHeadline.svelte
<script>
  import { scrollTextAction } from 'svg-scroll-draw/svelte';
  import { scrollAnimate }    from 'svg-scroll-draw/svelte';

  const eyebrowOpts = {
    props: { opacity: [0, 1], transform: ['translateY(10px)', 'translateY(0)'] },
    trigger: { start: 'top 90%', end: 'top 68%' },
    easing: 'ease-out', once: true,
  };
</script>

<div use:scrollAnimate={eyebrowOpts}>
  <span class="badge">New in v2</span>
</div>

<h1 use:scrollTextAction={{ split: 'words', stagger: 0.07, from: { opacity: 0, y: 36 }, once: true }}>
  Scroll animations without GSAP.
</h1>

<p use:scrollTextAction={{ split: 'chars', stagger: 0.012, from: { opacity: 0 }, once: true }}>
  Vue · Svelte · Solid · React · ~9 KB
</p>

6. Nuxt & Astro

Nuxt 3

Import from svg-scroll-draw/nuxt — it re-exports all v2 composables and components, and provides a plugin for global registration:

plugins/svg-scroll-draw.ts
import { createScrollDrawPlugin } from 'svg-scroll-draw/nuxt';

export default defineNuxtPlugin((nuxtApp) => {
  // Registers <ScrollDraw>, <ScrollAnimate>, <ScrollCounter>,
  // <ScrollVideo>, <ScrollText> globally — no per-component imports.
  nuxtApp.vueApp.use(createScrollDrawPlugin());
});

Astro

Use data-attributes for zero-import server components. initScrollAnimate and initScrollText are new in v2.3:

src/pages/index.astro
---
// No imports needed server-side
---

<h1
  data-scroll-text
  data-scroll-text-options='{"split":"words","stagger":0.05,"once":true}'
>
  Animate on scroll.
</h1>

<div
  data-scroll-animate
  data-scroll-animate-options='{"props":{"opacity":[0,1],"transform":["translateY(40px)","translateY(0)"]},"easing":"ease-out","once":true}'
>
  Fades and slides in.
</div>

<script>
  import { initAll } from 'svg-scroll-draw/astro';
  // Initialises scrollDraw, scrollAnimate, scrollText, scrollCounter in one call
  initAll();
</script>

Summary

FrameworkPatternImport
Vue 3useScrollAnimate(options) → refsvg-scroll-draw/vue
Vue 3<ScrollAnimate :options="...">svg-scroll-draw/vue
Svelteuse:scrollAnimate={opts}svg-scroll-draw/svelte
SveltecreateScrollAnimate → { action, getInstance }svg-scroll-draw/svelte
SoliduseScrollAnimate(options) → ref settersvg-scroll-draw/solid
SolidcreateScrollAnimate → { ref, getInstance }svg-scroll-draw/solid
NuxtSame as Vue + plugin factorysvg-scroll-draw/nuxt
Astrodata-scroll-animate + initScrollAnimate()svg-scroll-draw/astro
← Back to blogsvg-scroll-draw · MIT · ~9 KB