Motion Path

Animate elements along SVG paths with auto-rotation and alignment.

Motion path moves an element along an SVG path during an animation. The element travels from one point on the path to another, optionally rotating to follow the direction of travel.

Basic Usage

Add a path object inside to (or from). Set target to a CSS selector pointing to a <path> element in the DOM.

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

Motion("follow", ".dot", {
  to: { path: { target: "#track" } },
  duration: 2,
  ease: "power2.inOut",
}).onPageLoad();

The element moves from the start of the path (0) to the end (1) over the animation duration.


path Config

The path object is set as a property inside from or to.

PropertyTypeDefaultDescription
targetstring | ElementRequired. CSS selector pointing to a DOM <path>, or raw SVG path data (a string starting with M or m)
alignstring | ElementSnap the element onto the path by aligning its bounding box to this selector or element. Use "self" to align to the animated element itself
alignAt[number, number][50, 50]The origin point on the element that aligns to the path, as [x%, y%] percentages
startnumber0Path start position as a fraction 0–1
endnumber1Path end position as a fraction 0–1
rotatebooleanWhen true, the element auto-rotates to follow the tangent direction of the path

Targeting a DOM Path

Point target at a <path> element already in the SVG markup. The element is invisible to the user — it only guides the animation.

typescript
// HTML: <svg><path id="route" d="M 0 100 C 150 0 300 200 450 100" /></svg>

Motion("car", ".car-icon", {
  to: { path: { target: "#route", rotate: true } },
  duration: 3,
  ease: "power1.inOut",
}).onPageLoad();

Raw SVG Path Data

Pass a path d attribute string directly as target — no DOM element required. The string must start with M or m.

typescript
Motion("wave", ".particle", {
  to: {
    path: {
      target: "M 0 200 C 100 100 200 300 300 200 C 400 100 500 300 600 200",
      rotate: false,
    },
  },
  duration: 3,
  ease: "power2.inOut",
}).play();

Auto-Rotate

Set rotate: true to make the element turn so it always faces along the path’s direction of travel. Essential for arrows, cars, planes, or any element where orientation matters.

typescript
Motion("orbit", ".arrow", {
  to: { path: { target: "#orbit-path", rotate: true } },
  duration: 4,
  ease: "linear",
  repeat: -1,
}).play();

Align to Path

The align property snaps the element’s bounding box onto the path, so the animation starts exactly where the path begins. Without it, the element may appear to jump at the start of the animation.

typescript
Motion("badge", ".badge", {
  to: {
    path: {
      target: "#guide-path",
      align: "#guide-path",  // Align element origin to path origin
      rotate: true,
    },
  },
  duration: 2,
  ease: "power2.inOut",
}).onPageLoad();

Use "self" to align relative to the animated element itself:

typescript
to: {
  path: {
    target: "#route",
    align: "self",
  },
}

Origin Point with alignAt

alignAt controls which point on the element is placed on the path. Values are [x%, y%], defaulting to [50, 50] (center). Use [0, 0] to align by the top-left corner, or [50, 100] to align by the bottom-center.

typescript
to: {
  path: {
    target: "#route",
    align: "#route",
    alignAt: [50, 100],  // Anchor at bottom-center of the element
    rotate: true,
  },
}

Partial Path with start and end

Use start and end to animate over only a portion of the path. Both accept fractions from 0 to 1.

typescript
// Animate the middle third of the path
Motion("segment", ".dot", {
  to: {
    path: {
      target: "#full-path",
      start: 0.33,
      end: 0.66,
    },
  },
  duration: 1.5,
  ease: "power2.inOut",
}).onPageLoad();

This is useful for step-by-step reveals, progress indicators, or multi-stage animations where different elements cover different segments of the same path.


Scrubbed on Scroll

Combine motion path with .onScroll({ scrub: true }) to drive the element’s position along the path with the scrollbar.

typescript
Motion("scroll-path", ".marker", {
  to: {
    path: {
      target: "#timeline-path",
      rotate: true,
    },
  },
  duration: 1,
}).onScroll({
  scrub: true,
  start: "top center",
  end: "bottom center",
});

Looping Orbit

Use repeat: -1 with ease: "linear" for a smooth, infinite loop along a closed path.

typescript
Motion("orbit", ".satellite", {
  to: { path: { target: "#orbit-path", rotate: true } },
  duration: 6,
  ease: "linear",
  repeat: -1,
}).onPageLoad();

For a closed path (one where start and end points meet), "linear" easing ensures there is no speed variation and the loop is seamless.


Multi-Step Timeline

Animate multiple elements along paths in sequence using a multi-step timeline.

typescript
Motion("journey", "#map", [
  {
    target: ".marker",
    to: { path: { target: "#segment-a", rotate: true } },
    duration: 1.5,
    ease: "power2.inOut",
  },
  {
    target: ".marker",
    to: { path: { target: "#segment-b", rotate: true } },
    duration: 1.5,
    ease: "power2.inOut",
    position: "+=0.2",
  },
]).onPageLoad();

Tips

  • The SVG <path> can be hidden — set fill: none; stroke: none; visibility: hidden in CSS. It only needs to exist in the DOM for its d attribute to be read.
  • Raw path data is coordinate-space dependent — when passing a path string directly, make sure the coordinates match your layout. DOM path selectors are safer because coordinates are resolved from the element’s actual position.
  • rotate: true is essential for directional elements — arrows, vehicles, and cursors look wrong without it because they always point the same direction.
  • Combine with opacity — fade the element in at the start of a path animation to avoid a sudden appearance by adding from: { opacity: 0 } alongside to: { path: { ... } } using a multi-step timeline.
  • For pixel-perfect alignment use both align and alignAt together to control exactly which part of the element sits on the path.