Skip to content

phucbm/responsive-gsap

Repository files navigation

useGSAPResponsive

npm version npm downloads npm dependents github stars github license

A thin, powerful wrapper around useGSAP that adds responsive setup, automatic re-initialization, and optional “play after load” control — all while maintaining full compatibility with useGSAP best practices and return values.

✨ Features

  • Drop-in replacement for useGSAP — returns the same value and integrates seamlessly.
  • Responsive animation setups via media queries (gsap.matchMedia).
  • Auto re-setup on resize — re-runs your animation setup when specific elements change size.
  • Play-after-load control — delay animation playback until page loading is complete.
  • Safe cleanup — guarantees proper useGSAP cleanup for timelines, matchMedia, and observers.
  • Debug mode — optional console logs for setup, cleanup, and media triggers.

Installation

npm i responsive-gsap
pnpm add responsive-gsap

🚀 Usage

1. Single setup

For simple, non-responsive animations:

import {useGSAPResponsive} from "responsive-gsap";
import gsap from "gsap";
import {useRef} from "react";

export function Example() {
    const scope = useRef<HTMLDivElement>(null);

    useGSAPResponsive((root) => {
        const tl = gsap.timeline().from(root.querySelector(".box"), {x: -100, opacity: 0});
        return {timeline: tl};
    }, {scope});

    return (
        <div ref={scope}>
            <div className="box"/>
        </div>
    );
}

2. Responsive setups (media queries)

Run different animations per breakpoint with mediaQueries:

import {useGSAPResponsive} from "responsive-gsap";
import gsap from "gsap";
import {useRef} from "react";

export function Example() {
    const scope = useRef<HTMLDivElement>(null);

    useGSAPResponsive([
        {
            query: "(max-width: 768px)",
            setup: (root) => ({
                timeline: gsap.from(root.querySelector(".box"), {x: -50}),
            }),
        },
        {
            query: "(min-width: 769px)",
            setup: (root) => ({
                timeline: gsap.from(root.querySelector(".box"), {x: 100}),
            }),
        },
    ], {scope});

    return (
        <div ref={scope}>
            <div className="box"/>
        </div>
    );
}

Each setup cleans up automatically when the media condition changes.


3. Observe element resize

Re-run animation setup when a target element’s size changes:

import {useGSAPResponsive} from "responsive-gsap";
import gsap from "gsap";
import {useRef} from "react";

export function Example() {
    const scope = useRef<HTMLDivElement>(null);

    useGSAPResponsive(
        (root) => ({
            timeline: gsap.from(root.querySelector(".box"), {scale: 0.5}),
        }),
        {
            scope,
            observeResize: ".box",
        }
    );

    return (
        <div ref={scope}>
            <div className="box"/>
        </div>
    );
}

Useful for dynamic layouts or fluid containers.


4. Play-after-load (deferred animation)

Pause animation until a loading process completes:

import {useGSAPResponsive} from "responsive-gsap";
import gsap from "gsap";
import {useRef} from "react";

export function Example() {
    const scope = useRef<HTMLDivElement>(null);

    const loadingHandlers = {
        isLoadComplete: () => loadState === "done",
        isLoadingEnabled: () => true,
        onLoadComplete: (cb: () => void) => window.addEventListener("load", cb),
        offLoadComplete: (cb: () => void) => window.removeEventListener("load", cb),
    };

    useGSAPResponsive(
        (root) => ({
            timeline: gsap.timeline().from(root.querySelector(".box"), {y: 50, opacity: 0}),
        }),
        {
            scope,
            playAfterLoad: loadingHandlers,
        }
    );

    return (
        <div ref={scope}>
            <div className="box"/>
        </div>
    );
}

🧩 Notes

  • useGSAPResponsive inherits all behavior from useGSAP, including lifecycle and scope handling.
  • Always return { timeline, cleanup } from your setup for best control.
  • Media query and resize-based setups clean up correctly without manual handling.

🧠 Example integration

A responsive hero animation that waits for page load:

import {useGSAPResponsive} from "responsive-gsap";
import gsap from "gsap";
import {useRef} from "react";

export function Example() {
    const scope = useRef<HTMLDivElement>(null);

    const loadingHandlers = {
        isLoadComplete: () => document.readyState === "complete",
        isLoadingEnabled: () => true,
        onLoadComplete: (cb: () => void) => window.addEventListener("load", cb),
        offLoadComplete: (cb: () => void) => window.removeEventListener("load", cb),
    };

    useGSAPResponsive(
        [
            {
                query: "(max-width: 768px)",
                setup: (root) => ({
                    timeline: gsap.from(root.querySelector(".hero-title"), {y: 40, opacity: 0}),
                }),
            },
            {
                query: "(min-width: 769px)",
                setup: (root) => ({
                    timeline: gsap.from(root.querySelector(".hero-title"), {x: -100, opacity: 0}),
                }),
            },
        ],
        {
            scope,
            playAfterLoad: loadingHandlers,
            observeResize: ".hero-title",
            debug: true,
        }
    );

    return (
        <div ref={scope}>
            <h1 className="hero-title">Responsive GSAP</h1>
        </div>
    );
}

License

MIT © phucbm

About

A React hook that extends useGSAP with responsive media queries, resize observation, and load-controlled playback.

Topics

Resources

License

Stars

Watchers

Forks

Contributors