Reveal On Scroll

PreviousNext

Animate content into view on scroll with effects like fade, slide, zoom, and blur.

Docs
paceuiui

Preview

Loading preview…
gsap/reveal-on-scroll.tsx
"use client";

import { ComponentProps, useRef } from "react";

import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

type EffectType = "fadeIn" | "slideInRight" | "zoomIn" | "blurIn";
type RevealOnScrollProps = {
    effect?: EffectType;
    scrollTriggerVars?: ScrollTrigger.Vars;
    fromVars?: gsap.TweenVars;
    toVars?: gsap.TweenVars;
} & ComponentProps<"div">;

export const RevealOnScroll = ({
    effect = "fadeIn",
    scrollTriggerVars,
    fromVars,
    toVars,
    ...props
}: RevealOnScrollProps) => {
    const wrapperRef = useRef<HTMLDivElement | null>(null);
    const animationRef = useRef<gsap.core.Tween | null>(null);

    useGSAP(
        () => {
            const el = wrapperRef.current;
            if (!el) return;

            // Cleanup previous animation
            animationRef.current?.scrollTrigger?.kill();
            animationRef.current?.kill();
            gsap.set(el, { clearProps: "all" });

            const scrollTrigger: ScrollTrigger.Vars = {
                trigger: el,
                start: "top 80%",
                toggleActions: "play pause play reverse",
                ...scrollTriggerVars,
            };

            const presets: Record<EffectType, { from: gsap.TweenVars; to: gsap.TweenVars }> = {
                fadeIn: {
                    from: { opacity: 0, y: 50 },
                    to: { opacity: 1, y: 0, duration: 1 },
                },
                slideInRight: {
                    from: { x: 100, opacity: 0 },
                    to: { x: 0, opacity: 1, duration: 1 },
                },
                zoomIn: {
                    from: { scale: 0.8, opacity: 0 },
                    to: { scale: 1, opacity: 1, duration: 1 },
                },
                blurIn: {
                    from: { y: 30, opacity: 0, filter: "blur(10px)" },
                    to: { y: 0, opacity: 1, filter: "blur(0px)", duration: 1 },
                },
            };

            const preset = presets[effect] || presets.fadeIn;
            const fromVarsFinal = { ...preset.from, ...fromVars };
            const toVarsFinal = { ...preset.to, scrollTrigger, ...toVars };

            animationRef.current = gsap.fromTo(el, fromVarsFinal, toVarsFinal);

            return () => {
                animationRef.current?.scrollTrigger?.kill();
                animationRef.current?.kill();
            };
        },
        { scope: wrapperRef, dependencies: [effect, scrollTriggerVars, fromVars, toVars] },
    );

    return <div {...props} ref={wrapperRef} />;
};

Installation

npx shadcn@latest add @paceui/reveal-on-scroll

Usage

import { RevealOnScroll } from "@/components/ui/reveal-on-scroll"
<RevealOnScroll />