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.
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.
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.
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.
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.
// 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.
// 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 onDOMContentLoaded.'after'starts on the windowloadevent, 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.
// 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.
// 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.
Motion("pulse", ".badge", {
to: { scale: 1.08, opacity: 0.8 },
duration: 0.8,
ease: "sine.inOut",
})
.withRepeat({ times: -1, yoyo: true })
.onPageLoad();Related
- Scroll Trigger — play when elements enter the viewport
- Hover — react to mouse enter and leave
- Click — toggle animations on click
- Duration & Delay — control timing within animations
- Stagger — sequence multiple elements