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:

OptionTypeDefaultDescription
timing'before' | 'during' | 'after'Execution order when multiple .onPageLoad() timelines are registered. Earlier values play first.
pausedbooleanfalseBuild 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()
TimingWaits for DOMContentLoadedPlays immediately, no event listener
Use whenScript runs early (e.g. <head>, top of <body>)Script runs inside a lifecycle hook or after DOM is ready
Multiple timelinesSorts by timing option, plays in orderPlays immediately in call order
If DOM already readyFires immediately on registrationSame — 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.


Ordering Multiple Animations

When several timelines all use .onPageLoad(), the timing option controls their relative order. Timelines with 'before' play first, then 'during', then 'after'. Within the same group, registration order is preserved.

typescript
// Plays first — above-the-fold hero elements
Motion("hero", "#hero", {
  from: { opacity: 0, y: 50 },
  duration: 0.8,
}).onPageLoad({ timing: "before" });

// Plays after hero settles
Motion("nav", "nav", {
  from: { opacity: 0, y: -20 },
  duration: 0.5,
}).onPageLoad({ timing: "during" });

// Plays last — supporting elements
Motion("footer-cta", ".footer-cta", {
  from: { opacity: 0 },
  duration: 0.4,
}).onPageLoad({ timing: "after" });

SPA and Framework Usage

If your script runs after DOMContentLoaded has already fired — 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:

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();