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' | — | Execution order when multiple .onPageLoad() timelines are registered. Earlier values play first. |
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 | Waits for DOMContentLoaded | 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 | Sorts by timing option, plays in order | Plays immediately in call order |
| If DOM already ready | 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.
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.
// 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:
// 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