Real-world examples
Ten production-ready patterns — logo reveals, charts, signatures, diagrams, Timeline API, Group API, Sequence API, and more. Each one is powered by svg-scroll-draw and works in React, Next.js, Vue, and vanilla JS. Scroll down to see them draw live.
01
·onComplete · fill transitionThe Discord logo strokes itself in as an outline, then floods with color on completion — followed by a right-eye blink. Uses onComplete to trigger the fill transition and blink sequence.
// 1. stroke draws on scroll (fill starts transparent)
// 2. onComplete: fill floods in + right eye blinks
<ScrollDraw easing="ease-out" speed={0.95} once
onComplete={handleComplete}>
<svg viewBox="0 0 127.14 96.36">
<path d="M107.7,8.07…"
fill={filled ? '#5865F2' : 'transparent'}
stroke="#5865F2" strokeWidth="2.5"
style={{ transition: 'fill 0.45s ease' }} />
{/* Left eye — floods white on complete */}
<ellipse cx="42.45" cy="53.03" rx="11.42" ry="12.67"
fill={filled ? 'white' : 'transparent'}
style={{ transition: 'fill 0.45s ease' }} />
{/* Right eye — blinks via scaleY */}
<ellipse cx="84.69" cy="53.03" rx="11.42" ry="12.67"
fill={filled ? 'white' : 'transparent'}
style={{ transform: `scaleY(${blinking ? 0.05 : 1})`,
transformBox: 'fill-box', transformOrigin: 'center',
transition: 'fill 0.45s ease, transform 0.1s ease-in-out' }} />
</svg>
</ScrollDraw>02
·selector · strokeColorMonthly revenue line draws itself across the chart axes on scroll — axes animate first, then the data line traces in with a color shift from grey to brand pink.
<ScrollDraw
easing="ease-in-out" speed={0.85} once
selector=".ink"
strokeColor={['#e2e8f0', '#ff90e8']}
>
<svg>
{/* Static: grid, labels, data points, area */}
{/* .ink elements animate: axes + data line */}
<line className="ink" {/* y-axis */} />
<line className="ink" {/* x-axis */} />
<path className="ink" d="M 55 138 C…" {/* data line */} />
</svg>
</ScrollDraw>03
·stagger · ease-outCEO signature draws on scroll with a staggered underline flourish — label, printed name and date are static context. Looks like ink drying on a real document.
<ScrollDraw
easing="ease-out" speed={0.6}
stagger={0.35} once
>
<svg>
{/* Static: "Authorized Signature" label, name, date */}
{/* Animated path 1: cursive signature */}
<path d="M 18 90 C 14 62…" stroke="#111" strokeWidth="2.2" />
{/* Animated path 2: underline flourish (starts after sig) */}
<path d="M 12 112 C 90 122…" stroke="#ff90e8" />
</svg>
</ScrollDraw>04
·selector · staggerBox borders and arrow connectors draw in sequence across a 4-step checkout process. Static fills and emoji keep the diagram readable before animation starts.
<ScrollDraw
easing="ease-out" speed={1.1}
selector=".ink" stagger={0.18} once
>
<svg>
{/* Static: filled boxes + emoji + step labels */}
<rect x={8} fill="#f0f4ff" />{/* Cart */}
<rect x={82} fill="#fff8f0" />{/* Shipping */}
{/* .ink elements animate: borders + arrows */}
<rect className="ink" x={8} stroke="#5865F2" />
<path className="ink" d="M 70 70 L 82 70" {/* arrow */} />
</svg>
</ScrollDraw>05
·selector · strokeColorA delivery route traces itself across a city block map — warehouse to customer. City blocks, street names and the distance badge are static; only the route line animates.
<ScrollDraw
easing="ease-in-out" speed={0.75} once
selector=".ink"
strokeColor={['#fbbf24', '#ff90e8']}
>
<svg>
{/* Static: city blocks, street labels, distance badge */}
{/* Static: start/end markers */}
{/* Animated: route path only */}
<path className="ink"
d="M 44 175 L 44 132 L 90 132…"
stroke="#fbbf24" strokeWidth="4" />
</svg>
</ScrollDraw>06
·selector · ease-outConnection lines between services draw in on scroll — Browser → API Gateway → Auth / Database / Cache. Service boxes and labels are always visible; only the wires animate.
<ScrollDraw
easing="ease-out" speed={1.1} once
selector=".ink"
>
<svg>
{/* Static: labeled service boxes */}
<rect … /><text>🌐 Browser</text>
<rect … /><text>API Gateway</text>
{/* .ink lines draw the connections */}
<line className="ink" {/* Browser → API */} />
<line className="ink" {/* API → Auth */} strokeDasharray="4 3" />
<line className="ink" {/* API → DB */} strokeDasharray="4 3" />
<line className="ink" {/* API → Cache */} strokeDasharray="4 3" />
</svg>
</ScrollDraw>07
·data-scroll-draw · initScrollDraw()Zero-JS server components — add data-scroll-draw to any element and call initScrollDraw() in a client script. Options are passed as a JSON attribute. No framework wrapper needed.
// src/pages/index.astro
---
// No server-side imports needed
---
<div
data-scroll-draw
data-scroll-draw-options='{"easing":"ease-out","fade":true,"once":true}'
>
<svg viewBox="0 0 200 80" fill="none">
<path d="M10 40 Q100 5 190 40"
stroke="white" strokeWidth="2" />
</svg>
</div>
<script>
import { initScrollDraw } from 'svg-scroll-draw/astro';
// Finds all [data-scroll-draw] on the page and
// initialises each one — no React, no Vue needed.
initScrollDraw();
</script>08
·scrollDrawTimeline · independent tracksFour quarterly bars and a trend line — each animated on its own independent scroll window using scrollDrawTimeline. Axes draw first (0→28%), then Q1–Q4 bars stagger across (10→88%), and the trend line traces last (75→100%) once all bars are fully visible.
import { scrollDrawTimeline } from 'svg-scroll-draw/timeline';
// Each track owns its own from/to slice of the scroll range.
// Unlike stagger (time offset), windows can overlap freely.
scrollDrawTimeline('#chart', {
trigger: { start: 'top 88%', end: 'top 25%' },
tracks: [
{ selector: '.axis', from: 0, to: 0.28, easing: 'ease-out' },
{ selector: '.bar-1', from: 0.1, to: 0.42, easing: 'ease-out' },
{ selector: '.bar-2', from: 0.26, to: 0.56, easing: 'ease-out' },
{ selector: '.bar-3', from: 0.42, to: 0.72, easing: 'ease-out' },
{ selector: '.bar-4', from: 0.58, to: 0.88, easing: 'ease-out' },
// Trend line draws only after all bars are visible
{ selector: '.trend', from: 0.75, to: 1.0, easing: 'spring' },
],
});09
·scrollDrawGroup · synchronizedThree separate SVG containers — Speed, Size, and Framework icons — all animate simultaneously the moment the section scrolls into view. scrollDrawGroup wires them to the same scroll timeline with one call.
import { scrollDrawGroup } from 'svg-scroll-draw/group';
// All three containers share the same options and
// start drawing at exactly the same scroll position.
const group = scrollDrawGroup(
[speedRef.current, sizeRef.current, frameworkRef.current],
{
easing: 'ease-out',
speed: 1.1,
fade: true,
once: true,
trigger: { start: 'top 88%', end: 'top 25%' },
}
);
// One call controls all instances
group.replay();
group.pause();
group.destroy(); // cleanup on unmount10
·scrollDrawSequence · one after anotherThree steps — Code, Build, Ship — draw in strict sequence: each one starts only after the previous reaches 100%. The border and label color up as each step completes. Uses scrollDrawSequence with onComplete to track state.
import { scrollDrawSequence } from 'svg-scroll-draw/group';
// Each container starts drawing only after
// the previous one fully completes.
const seq = scrollDrawSequence(
[codeRef.current, buildRef.current, shipRef.current],
{
easing: 'ease-out',
speed: 1.4,
fade: true,
trigger: { start: 'top 88%', end: 'top 25%' },
onComplete: () => setStep((s) => s + 1),
}
);
seq.replay(); // restart from step 1
seq.destroy(); // cleanup on unmount01
Code
02
Build
03
Ship