Loaders
Preloader
Full-screen intro that counts to 100 while the name rises, then wipes upward.
npx bezel-add add preloaderAbout
A full-screen GSAP page preloader for React: a percentage counter runs from 0 to 100 while the letters of your name or brand rise into view. The middle letters then fade out and the whole screen slides up to reveal the page. Use it as a portfolio or agency intro, and start your own entrance animation from its completion callback.
Key props: name (text revealed letter by letter, default "LOADING"), onComplete (called once the screen has wiped away)
packages/ui/src/loaders/Preloader.tsx · 83 lines
"use client";
import { useEffect, useRef } from "react";
import gsap from "gsap";
type Props = {
name?: string;
onComplete: () => void;
};
export function Preloader({ name = "LOADING", onComplete }: Props) {
const rootRef = useRef<HTMLDivElement>(null);
const counterRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
const preloader = rootRef.current;
const counterEl = counterRef.current;
if (!preloader || !counterEl) return;
const chars = Array.from(preloader.querySelectorAll<HTMLElement>(".yui-pl-char"));
const obj = { value: 0 };
const tl = gsap.timeline({
onComplete: () => {
gsap.delayedCall(0.1, onComplete);
},
});
// Count 0 → 100
tl.to(obj, {
value: 100,
duration: 1.6,
ease: "power2.inOut",
onUpdate: () => {
counterEl.textContent = Math.round(obj.value) + "%";
},
});
// Reveal letters
tl.to(chars, { y: 0, duration: 1, stagger: 0.035, ease: "power3.out" }, 0.2);
// Fade non-key chars then slide preloader up
tl.to(chars.slice(1, -1), { opacity: 0, y: "-20%", duration: 0.5, stagger: 0.02, ease: "power2.in" }, "+=0.3");
tl.to(preloader, { yPercent: -100, duration: 0.9, ease: "power3.inOut" }, "-=0.2");
return () => { tl.kill(); };
}, [onComplete]);
const letters = name.split("").map((ch, i) => (
<span key={i} className="yui-pl-char" style={{ display: "inline-block", transform: "translateY(110%)" }}>
{ch === " " ? "\u00A0" : ch}
</span>
));
return (
<>
<style>{`
.yui-pl-root {
position: fixed; top: 0; left: 0;
width: 100%; height: 100%;
z-index: 10000; background: #fff;
display: flex; justify-content: center; align-items: center;
}
.yui-pl-name {
display: flex; overflow: hidden;
color: #000;
font-size: clamp(3rem, 8vw, 12rem);
font-weight: 500; letter-spacing: -0.05em;
}
.yui-pl-counter {
position: fixed; right: 5rem; bottom: 5rem;
font-size: clamp(4rem, 6vw, 10rem);
font-weight: 500; color: #000; overflow: hidden;
}
`}</style>
<div className="yui-pl-root" ref={rootRef}>
<div className="yui-pl-name">{letters}</div>
<div className="yui-pl-counter">
<span ref={counterRef}>0%</span>
</div>
</div>
</>
);
}