Page Load Trigger

Auto-play animations when the page loads or DOM is ready.

.onPageLoad() plays a timeline as soon as the DOM is ready — the go-to trigger for entrance animations, hero reveals, and anything that should animate in when the page first appears.

Basic Usage

Chain .onPageLoad() after defining your animation. The timeline plays automatically when DOMContentLoaded fires.

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

Motion("hero", "#hero", {
  from: { opacity: 0, y: 40 },
  duration: 0.8,
  ease: "power2.out",
}).onPageLoad();

Options

.onPageLoad() accepts an optional config object:

Option Type Default Description
timing 'before' | 'during' | 'after' 'during' Lifecycle event that starts playback: immediately, on DOMContentLoaded, or on window.load.
paused boolean false Build the timeline and apply initial states immediately, but don’t auto-play. Start manually with Motion(name).play().

.play() — Immediate Trigger

.play() plays the timeline right now, with no event listener involved. Use it when you’re calling animation code inside a framework lifecycle hook (useEffect, onMounted, Astro page-load) or after an explicit user action, where the DOM is already ready.

typescript
Motion("hero", "#hero", {
  from: { opacity: 0, y: 40 },
  duration: 0.8,
  ease: "power2.out",
}).play();

onPageLoad vs play — When to Use Each

.onPageLoad() .play()
Timing Defaults to DOMContentLoaded; configurable with timing Plays immediately, no event listener
Use when Script runs early (e.g. <head>, top of <body>) Script runs inside a lifecycle hook or after DOM is ready
Multiple timelines Each starts at its selected lifecycle event Plays immediately in call order
If its event already fired Fires immediately on registration Same — fires immediately

Practical rule: if your code runs inside window.addEventListener('DOMContentLoaded', ...), a framework useEffect, or an Astro page-load event — use .play(). If your <script> tag is in <head> and runs before the DOM is parsed — use .onPageLoad().


With Delay

Add a delay (in seconds) to the AnimationConfig to hold before the animation begins. This is useful for staggering multiple independent animations or letting the page “settle” before an entrance.

typescript
Motion("tagline", ".tagline", {
  from: { opacity: 0, y: 20 },
  duration: 0.6,
  delay: 0.4,
  ease: "power2.out",
}).onPageLoad();

Staggered Entrance

Apply stagger to animate multiple elements in sequence — the classic staggered card or list reveal on load.

typescript
Motion("cards", ".feature-card", {
  from: { opacity: 0, y: 30, scale: 0.95 },
  duration: 0.7,
  ease: "power3.out",
  stagger: { each: 0.1, from: "start" },
}).onPageLoad();


From-Only Pattern

The most common page-load pattern is a from-only reveal: start hidden or offset, animate back to the element’s natural CSS state. The SDK reads the natural to values automatically.

typescript
// Slide up from below — SDK auto-resolves to: { opacity: 1, y: 0 }
Motion("hero", ".hero-title", {
  from: { opacity: 0, y: 60 },
  duration: 1,
  ease: "power3.out",
}).onPageLoad();

// Scale up from slightly smaller
Motion("cta", ".cta-button", {
  from: { opacity: 0, scale: 0.85 },
  duration: 0.5,
  ease: "back.out",
  delay: 0.6,
}).onPageLoad();

Paused Timeline — Manual Trigger

Set paused: true to build the timeline and apply initial states immediately without playing. Trigger it later with an explicit .play() call.

typescript
// Register on load — applies from-state immediately, does not play
Motion("modal", ".modal", {
  from: { opacity: 0, scale: 0.92 },
  duration: 0.4,
  ease: "power2.out",
}).onPageLoad({ paused: true });

// Play later — e.g. after a button click
document.querySelector("#open-btn").addEventListener("click", () => {
  Motion("modal").play();
});

This is useful for modals, drawers, or any element that should start in its hidden state and only animate in on demand.


Lifecycle Timing

The timing option selects a real document lifecycle point; it does not wait for an earlier animation to finish:

  • 'before' starts immediately when .onPageLoad() is registered.
  • 'during' (default) starts on DOMContentLoaded.
  • 'after' starts on the window load event, after dependent resources finish loading.

If the selected event has already fired, that timeline starts immediately on registration. Timelines waiting for the same event are started in registration order and can run concurrently.

Killing a waiting timeline unregisters it before the lifecycle event can play it. When the last pending page-load timeline is killed, replaced with another trigger, or removed by Motion.killAll(), the SDK also removes its pending DOMContentLoaded and load listeners. A later .onPageLoad() registration safely attaches a fresh listener only for lifecycle phases that have not fired yet.

typescript
// Starts immediately when registered
Motion("hero", "#hero", {
  from: { opacity: 0, y: 50 },
  duration: 0.8,
}).onPageLoad({ timing: "before" });

// Starts when the DOMContentLoaded event fires
Motion("nav", "nav", {
  from: { opacity: 0, y: -20 },
  duration: 0.5,
}).onPageLoad({ timing: "during" });

// Starts when the window load event fires
Motion("footer-cta", ".footer-cta", {
  from: { opacity: 0 },
  duration: 0.4,
}).onPageLoad({ timing: "after" });

SPA and Framework Usage

If your script runs after the selected lifecycle event — inside a React useEffect, Vue onMounted, or Astro page-load event — .onPageLoad() fires immediately on registration. In these contexts, .play() is often the clearer choice. One exception is timing: "after" registered after DOMContentLoaded but before window.load; it still waits for window.load.

typescript
// React
useEffect(() => {
  Motion("hero", ".hero", { from: { opacity: 0, y: 30 }, duration: 0.8 }).play();
}, []);

// Astro (View Transitions)
document.addEventListener("astro:page-load", () => {
  Motion("hero", ".hero", { from: { opacity: 0, y: 30 }, duration: 0.8 }).play();
});

For SPA contexts where animations need to be torn down and re-initialized on route change, wrap them in Motion.context() for clean lifecycle management. See Dynamic Content & SPA Pattern for the full pattern.


Looping on Page Load

Chain .withRepeat() before .onPageLoad() to loop the entire timeline continuously after the page loads.

typescript
Motion("pulse", ".badge", {
  to: { scale: 1.08, opacity: 0.8 },
  duration: 0.8,
  ease: "sine.inOut",
})
  .withRepeat({ times: -1, yoyo: true })
  .onPageLoad();