Retour au catalogue

Process Path Draw

SVG path qui se dessine entre les etapes au scroll (stroke-dashoffset anime via useScroll). Cercles numerotes relies par un trait.

processcomplex Both Responsive a11y
elegantminimaluniversalsaasagencystacked
Theme

How to build a scroll-driven SVG path draw in React

To animate an SVG path drawing itself on scroll in React, use Framer Motion's useScroll to get a scrollYProgress value, map it to a pathLength motion value with useTransform, and apply it to a motion.path element. The stroke appears to draw in as the section scrolls into view.

  • Stack: React + Framer Motion 11 + CSS custom properties, ~46 lines, zero extra dependencies.
  • Core APIs: useScroll, useTransform, motion.path, pathLength motion value.
  • The SVG path is generated dynamically from the steps array, so adding a step extends the line automatically.
  • Accessible: step titles and descriptions are plain DOM text; the SVG is decorative (no aria role needed).
  • Responsive by default. On narrow viewports the container max-width and padding-left scale the layout correctly.

Process Path Draw is a vertical process timeline where a line connecting numbered circles traces itself as you scroll. The drawing effect replaces a static bullet list with a motion that mirrors the idea of progress: each step reveals as you reach it. It pairs naturally with onboarding flows, agency case studies, or product how-it-works sections.

Anatomy

A full-width section contains a centered header (h2 + subtitle) and a constrained column (max 680px). Inside that column, an SVG sits absolutely on the left edge, 2rem wide, spanning the full column height. It renders two overlapping paths with identical d attributes: a grey static path as the track, and an accent-colored motion.path whose pathLength animates from 0 to 1. Step cards sit to the right of the SVG, each with an absolutely-positioned numbered circle that aligns to the path.

How it works

The key is Framer Motion's pathLength motion value. A ref on the outer container is passed to useScroll with offset ['start 0.75', 'end 0.6'], which makes scrollYProgress go from 0 to 1 as the container enters and crosses the viewport. useTransform maps that 0-1 range directly to a pathLength 0-1 range. Applying this to a motion.path element makes Framer Motion animate the stroke-dashoffset CSS property under the hood, which is the native browser mechanism for drawing SVG paths. Each step card also fades in with whileInView, staggered by index.

How to build it in React

  1. Define the path geometry from your steps

    Build the SVG d attribute by mapping your steps array. Each step is spaced 180 units apart vertically on a 100-unit-wide viewBox. A fixed cx=50 keeps the line centered on the 2rem SVG column.

    const cx = 50;
    const pathD = steps
      .map((_, i) => `${i === 0 ? "M" : "L"} ${cx} ${i * 180 + 20}`)
      .join(" ");
  2. Bind scroll progress to pathLength

    Attach a ref to the container div and pass it to useScroll. The offset array controls at which scroll positions the animation starts and ends. useTransform converts the raw 0-1 scrollYProgress into the pathLength motion value.

    const containerRef = useRef<HTMLDivElement>(null);
    const { scrollYProgress } = useScroll({
      target: containerRef,
      offset: ["start 0.75", "end 0.6"],
    });
    const pathLength = useTransform(scrollYProgress, [0, 1], [0, 1]);
  3. Render the two-layer SVG

    Place an absolute SVG next to the steps column. Draw the grey static path first as a visual rail, then the motion.path with the pathLength style. Both use the same d string. Set preserveAspectRatio='none' so the line stretches to match the actual column height regardless of step count.

    <motion.path
      d={pathD}
      stroke="var(--color-accent)"
      strokeWidth="3"
      strokeLinecap="round"
      style={{ pathLength }}
    />
  4. Align step bullets to the line

    Each step card has position:relative. Inside it, an absolutely-positioned circle sits at left:-4.5rem (matching the SVG column width plus gap). A double box-shadow ring, background color then accent color, creates the 'knot on the line' visual without any extra DOM element.

When to use it

Reach for Process Path Draw when you have 3 to 5 sequential steps and want to give each step visual weight as the user scrolls. It works well for onboarding explanations, methodology sections on agency sites, and product how-it-works flows. Skip it when steps are truly parallel or when the page is short enough that the scroll distance won't trigger the animation meaningfully. On pages with many other scroll animations, test the combined scroll budget to avoid jank.

Used by

  • Stripe, Uses vertical path and connector lines in product explainer sections to guide the eye through a multi-step payment flow.
  • Linear, Animated connectors between workflow steps appear on its features and changelog pages, reinforcing the idea of sequenced progress.
  • Lottiefiles, Path-draw timeline animations are a showcase format on the platform, demonstrating the technique in marketing pages.

FAQ

How do I control the speed of the drawing animation?

Adjust the offset array in useScroll. A tighter range like ['start 0.6', 'end 0.8'] makes the draw happen faster over a shorter scroll distance. Widening it to ['start 0.9', 'end 0.2'] slows it down across a longer scroll range.

Can I use a curved path instead of straight lines?

Yes. Replace the M/L commands in pathD with bezier curves using C or Q commands. The pathLength animation works on any valid SVG path, so the technique is geometry-agnostic.

Why does the SVG use preserveAspectRatio='none'?

The viewBox height is computed from step count (steps.length * 180), but the rendered SVG height is 100% of the container. Without preserveAspectRatio='none', the browser would letterbox or clip the path. The 'none' value stretches the path geometry to fill the element exactly.

Does the animation replay when the user scrolls back up?

The pathLength is a live motion value tied to scroll position, so it naturally reverses when scrolling back up. The step cards use whileInView with once:true, so they stay visible after their first appearance.

"use client";

import { useRef } from "react";
import { motion, useScroll, useTransform } from "framer-motion";

interface Step { number: number; title: string; description: string }
interface ProcessPathDrawProps { title?: string; subtitle?: string; steps?: Step[] }

const E = [0.16, 1, 0.3, 1] as const;
const HEAD: React.CSSProperties = { fontFamily: "var(--font-sans)", fontSize: "clamp(2rem, 4vw, 3rem)", fontWeight: 700, lineHeight: 1.1, letterSpacing: "-0.02em", color: "var(--color-foreground)", marginBottom: "1rem" };

export default function ProcessPathDraw({ title = "Notre demarche", subtitle = "Un chemin clair vers votre succes.", steps = [] }: ProcessPathDrawProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const { scrollYProgress } = useScroll({ target: containerRef, offset: ["start 0.75", "end 0.6"] });
  const pathLength = useTransform(scrollYProgress, [0, 1], [0, 1]);
  const cx = 50;
  const pathD = steps.map((_, i) => `${i === 0 ? "M" : "L"} ${cx} ${i * 180 + 20}`).join(" ");

  return (
    <section style={{ paddingTop: "var(--section-padding-y-lg)", paddingBottom: "var(--section-padding-y-lg)", background: "var(--color-background)" }}>
      <div style={{ maxWidth: "var(--container-max-width)", margin: "0 auto", padding: "0 var(--container-padding-x)" }}>
        <div style={{ textAlign: "center", marginBottom: "4rem" }}>

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reviews

React SVG Path Draw on Scroll, Process Timeline