SVG Animations

Draw SVG strokes with DrawSVG and animate stroke and fill colors.

Animate SVG icons and illustrations by progressively revealing strokes with drawSVG, or by transitioning stroke and fill colors. These properties work on any SVG element with a stroke — <path>, <circle>, <line>, <polyline>, and so on.

DrawSVG

drawSVG progressively reveals or hides an SVG stroke by controlling where the visible portion starts and ends along the path. Pass it inside from or to.

typescript
import { Motion } from "@motion.page/sdk";

// Draw a stroke in from hidden to fully visible
Motion("draw-logo", "#logo path", {
  from: { drawSVG: "0% 0%" },
  to: { drawSVG: "0% 100%" },
  duration: 1.5,
  ease: "power2.inOut",
}).onPageLoad();

Because "0% 100%" (fully visible) is the natural drawn state, you can use from-only and let the SDK resolve the endpoint from the element’s current state:

typescript
// Simpler — from hidden, animate to natural (fully drawn) state
Motion("draw-logo", "#logo path", {
  from: { drawSVG: "0% 0%" },
  duration: 1.5,
  ease: "power2.inOut",
}).onPageLoad();

DrawSVG Format

The drawSVG value defines a window along the stroke — two points (start and end) expressed as percentages or pixels.

ValueMeaning
"0% 100%"Full stroke visible (natural state)
"0% 0%"Fully hidden — good draw-in start
"100% 100%"Fully hidden from the other end
"20% 80%"Middle portion only — edges hidden
"50%"Shorthand for "0% 50%" — first half visible
"100px 500px"Pixel range along the stroke
typescript
// Reveal from the end (wipe-out effect)
Motion("wipe", ".icon path", {
  from: { drawSVG: "100% 100%" },
  to: { drawSVG: "0% 100%" },
  duration: 0.8,
  ease: "power2.out",
}).onScroll({ toggleActions: "play none none none" });

// Reveal middle portion
Motion("partial", "circle", {
  from: { drawSVG: "0% 0%" },
  to: { drawSVG: "20% 80%" },
  duration: 1,
}).onPageLoad();

Object Format

Pass an object with start and end keys. Values are percentages 0–100 (not 0–1 fractions).

typescript
// Object format — same as "20% 80%"
Motion("draw-arc", "path", {
  from: { drawSVG: { start: 0, end: 0 } },
  to:   { drawSVG: { start: 20, end: 80 } },
  duration: 1.2,
  ease: "power2.inOut",
}).onPageLoad();

Caution: { start: 0.2, end: 0.8 } is not 20%–80% — these are percentages, so 20 and 80 are the correct values. Using 0.2 / 0.8 would give you 0.2%–0.8%, a nearly invisible sliver.

Stroke Color

stroke sets and animates the color of the line drawn around an SVG element. Pass any CSS color string.

typescript
// Animate stroke color on hover
Motion("stroke-hover", ".icon", {
  to: { stroke: "#6633EE" },
  duration: 0.3,
  ease: "power2.out",
}).onHover({ each: true, onLeave: "reverse" });
typescript
// Transition stroke color on page load
Motion("stroke-in", "path", {
  from: { stroke: "#cccccc" },
  to:   { stroke: "#ff4444" },
  duration: 0.8,
}).onPageLoad();

Because stroke reads the element’s current CSS as the missing endpoint, from-only and to-only both work correctly.

Fill Color

fill animates the color painted inside an SVG shape. Same behavior as stroke — pass any CSS color string, and use from or to depending on whether the natural CSS is the start or end state.

typescript
// Fill a shape on scroll
Motion("fill-in", ".icon-shape", {
  from: { fill: "transparent" },
  to:   { fill: "#6633EE" },
  duration: 0.6,
  ease: "power2.out",
}).onScroll({ toggleActions: "play none none none" });
typescript
// Hover color change
Motion("fill-hover", ".nav-icon path", {
  to: { fill: "#0099ff" },
  duration: 0.2,
}).onHover({ each: true, onLeave: "reverse" });

Combining DrawSVG and Color

drawSVG, stroke, and fill all work in the same animation config. Use a multi-step timeline to draw the stroke first, then fill the shape.

typescript
// Draw the stroke, then fill — sequential with position offset
Motion("draw-and-fill", "#logo", [
  {
    target: "#logo path",
    from: { drawSVG: "0% 0%", stroke: "#aaaaaa" },
    to:   { drawSVG: "0% 100%", stroke: "#6633EE" },
    duration: 1.5,
    ease: "power2.inOut",
  },
  {
    target: "#logo path",
    from: { fill: "transparent" },
    to:   { fill: "#6633EE" },
    duration: 0.6,
    ease: "power2.out",
    position: "+=0.1",
  },
]).onPageLoad();

Or animate them in parallel within a single config:

typescript
// Draw and color-shift simultaneously
Motion("draw-color", "path", {
  from: { drawSVG: "0% 0%", stroke: "#cccccc" },
  to:   { drawSVG: "0% 100%", stroke: "#6633EE" },
  duration: 1.2,
  ease: "power2.inOut",
}).onPageLoad();

Common Patterns

Logo Draw-In

Stagger multiple paths for a sequential logo reveal. Target all paths in the SVG and use stagger to offset each one.

typescript
Motion("logo-draw", "#logo path", {
  from: { drawSVG: "0% 0%" },
  duration: 1,
  ease: "power2.inOut",
  stagger: 0.15,
}).onPageLoad();

Icon Reveal on Scroll

Trigger the draw when the icon enters the viewport. Each icon animates independently with each: true.

typescript
Motion("icon-reveal", ".feature-icon path", {
  from: { drawSVG: "0% 0%", opacity: 0 },
  duration: 0.8,
  ease: "power2.out",
  stagger: 0.1,
}).onScroll({ each: true, toggleActions: "play none none none" });

Progress Indicator

Scrub drawSVG against scroll to show reading progress or step completion.

typescript
Motion("progress", "#progress-ring circle", {
  from: { drawSVG: "0% 0%" },
  to:   { drawSVG: "0% 100%" },
  duration: 1,
}).onScroll({
  scrub: true,
  start: "top top",
  end: "bottom bottom",
});

Animated Underline on Hover

Draw a stroke underline from left to right on hover, reverse on leave.

typescript
Motion("underline", ".nav-link", {
  from: { drawSVG: "0% 0%" },
  to:   { drawSVG: "0% 100%" },
  duration: 0.35,
  ease: "power2.out",
}).onHover({ each: true, onLeave: "reverse" });

Draw with Yoyo Loop

Loop a stroke drawing and erasing for a continuous loading or attention indicator.

typescript
Motion("loading-ring", ".spinner circle", {
  from: { drawSVG: "0% 0%" },
  to:   { drawSVG: "0% 100%" },
  duration: 1,
  ease: "power1.inOut",
  repeat: { times: -1, yoyo: true },
}).onPageLoad();

Tips

  • SVG elements must have a stroke CSS property set (either inline or via CSS) for drawSVG to work — it manipulates stroke-dasharray and stroke-dashoffset under the hood.
  • fill: "none" on a path means the interior is transparent. Animate from "none" to a color by setting from: { fill: "none" } explicitly.
  • stroke-linecap: round on the SVG element produces smoother draw-in effects at the start and end of strokes.
  • Use transformOrigin to control the center of rotation if you also rotate SVG elements during the draw animation.