AgentStack
SKILL verified MIT Self-run

Gsap React

skill-bbeierle12-skill-mcp-claude-gsap-react · by Bbeierle12

GSAP integration with React including useGSAP hook, ref handling, cleanup patterns, and context management. Use when implementing GSAP animations in React components, handling component lifecycle, or building reusable animation hooks.

No reviews yet
0 installs
10 views
0.0% view→install

Install

$ agentstack add skill-bbeierle12-skill-mcp-claude-gsap-react

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Gsap React? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

GSAP React Integration

React-specific patterns for GSAP animations.

Quick Start

npm install gsap @gsap/react
import { useGSAP } from '@gsap/react';
import gsap from 'gsap';

function Component() {
  const containerRef = useRef(null);

  useGSAP(() => {
    gsap.to('.box', { x: 200, duration: 1 });
  }, { scope: containerRef });

  return (
    
      Animated
    
  );
}

useGSAP Hook

Basic Usage

import { useGSAP } from '@gsap/react';
import gsap from 'gsap';

function AnimatedComponent() {
  const container = useRef(null);

  useGSAP(() => {
    // All GSAP animations here
    gsap.from('.item', {
      opacity: 0,
      y: 50,
      stagger: 0.1
    });
  }, { scope: container }); // Scope limits selector queries

  return (
    
      Item 1
      Item 2
      Item 3
    
  );
}

With Dependencies

function AnimatedComponent({ isOpen }) {
  const container = useRef(null);

  useGSAP(() => {
    gsap.to('.drawer', {
      height: isOpen ? 'auto' : 0,
      duration: 0.3
    });
  }, { scope: container, dependencies: [isOpen] });

  return (
    
      Content
    
  );
}

Returning Context

function Component() {
  const container = useRef(null);

  const { context, contextSafe } = useGSAP(() => {
    gsap.to('.box', { x: 200 });
  }, { scope: container });

  // Use contextSafe for event handlers
  const handleClick = contextSafe(() => {
    gsap.to('.box', { rotation: 360 });
  });

  return (
    
      Click me
    
  );
}

Ref Patterns

Single Element Ref

function SingleElement() {
  const boxRef = useRef(null);

  useGSAP(() => {
    gsap.to(boxRef.current, {
      x: 200,
      rotation: 360,
      duration: 1
    });
  });

  return Box;
}

Multiple Element Refs

function MultipleElements() {
  const itemsRef = useRef([]);

  useGSAP(() => {
    gsap.from(itemsRef.current, {
      opacity: 0,
      y: 30,
      stagger: 0.1
    });
  });

  return (
    
      {[1, 2, 3].map((item, i) => (
         itemsRef.current[i] = el}
        >
          Item {item}
        
      ))}
    
  );
}

Dynamic Refs

function DynamicList({ items }) {
  const itemsRef = useRef(new Map());

  useGSAP(() => {
    gsap.from(Array.from(itemsRef.current.values()), {
      opacity: 0,
      y: 20,
      stagger: 0.05
    });
  }, { dependencies: [items.length] });

  return (
    
      {items.map(item => (
         {
            if (el) itemsRef.current.set(item.id, el);
            else itemsRef.current.delete(item.id);
          }}
        >
          {item.name}
        
      ))}
    
  );
}

Context and Cleanup

Automatic Cleanup

// useGSAP automatically cleans up animations on unmount
function Component() {
  useGSAP(() => {
    // This timeline is automatically killed on unmount
    gsap.timeline()
      .to('.a', { x: 100 })
      .to('.b', { x: 100 });
  });
}

Manual Context (Without useGSAP)

import gsap from 'gsap';

function Component() {
  useEffect(() => {
    const ctx = gsap.context(() => {
      gsap.to('.box', { x: 200 });
      gsap.to('.circle', { rotation: 360 });
    });

    return () => ctx.revert(); // Cleanup
  }, []);
}

Scoped Context

function Component() {
  const containerRef = useRef(null);

  useEffect(() => {
    const ctx = gsap.context(() => {
      // Selectors only query within containerRef
      gsap.to('.item', { opacity: 1 });
    }, containerRef);

    return () => ctx.revert();
  }, []);
}

Event Handlers

contextSafe for Events

function InteractiveComponent() {
  const container = useRef(null);

  const { contextSafe } = useGSAP(() => {
    // Initial animation
    gsap.set('.box', { scale: 1 });
  }, { scope: container });

  const handleMouseEnter = contextSafe(() => {
    gsap.to('.box', { scale: 1.1, duration: 0.2 });
  });

  const handleMouseLeave = contextSafe(() => {
    gsap.to('.box', { scale: 1, duration: 0.2 });
  });

  return (
    
      
        Hover me
      
    
  );
}

useCallback Alternative

function Component() {
  const boxRef = useRef(null);
  const tweenRef = useRef(null);

  const animateBox = useCallback(() => {
    tweenRef.current?.kill();
    tweenRef.current = gsap.to(boxRef.current, {
      x: '+=50',
      duration: 0.3
    });
  }, []);

  useEffect(() => {
    return () => tweenRef.current?.kill();
  }, []);

  return Click;
}

Timeline Management

Timeline Ref Pattern

function TimelineComponent() {
  const container = useRef(null);
  const tl = useRef(null);

  useGSAP(() => {
    tl.current = gsap.timeline({ paused: true })
      .to('.box', { x: 200 })
      .to('.box', { y: 100 })
      .to('.box', { rotation: 360 });
  }, { scope: container });

  const play = () => tl.current?.play();
  const reverse = () => tl.current?.reverse();
  const restart = () => tl.current?.restart();

  return (
    
      Animated
      Play
      Reverse
      Restart
    
  );
}

Controlled Timeline

function ControlledAnimation({ progress }) {
  const container = useRef(null);
  const tl = useRef(null);

  useGSAP(() => {
    tl.current = gsap.timeline({ paused: true })
      .to('.element', { x: 500 })
      .to('.element', { y: 200 });
  }, { scope: container });

  // Update timeline progress when prop changes
  useEffect(() => {
    if (tl.current) {
      tl.current.progress(progress);
    }
  }, [progress]);

  return (
    
      Controlled
    
  );
}

ScrollTrigger in React

Basic ScrollTrigger

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

gsap.registerPlugin(ScrollTrigger);

function ScrollComponent() {
  const container = useRef(null);

  useGSAP(() => {
    gsap.from('.section', {
      opacity: 0,
      y: 100,
      scrollTrigger: {
        trigger: '.section',
        start: 'top 80%',
        toggleActions: 'play none none none'
      }
    });
  }, { scope: container });

  return (
    
      Scroll to reveal
    
  );
}

ScrollTrigger Cleanup

function ScrollComponent() {
  const container = useRef(null);

  useGSAP(() => {
    const triggers = [];

    gsap.utils.toArray('.item').forEach(item => {
      const trigger = ScrollTrigger.create({
        trigger: item,
        start: 'top 80%',
        onEnter: () => gsap.to(item, { opacity: 1 })
      });
      triggers.push(trigger);
    });

    // Return cleanup function
    return () => triggers.forEach(t => t.kill());
  }, { scope: container });
}

Custom Hooks

useAnimation Hook

function useAnimation(animation, deps = []) {
  const elementRef = useRef(null);
  const tweenRef = useRef(null);

  useGSAP(() => {
    if (elementRef.current) {
      tweenRef.current = animation(elementRef.current);
    }
    return () => tweenRef.current?.kill();
  }, { dependencies: deps });

  return elementRef;
}

// Usage
function Component() {
  const boxRef = useAnimation((el) =>
    gsap.from(el, { opacity: 0, y: 50, duration: 0.5 })
  );

  return Animated;
}

useFadeIn Hook

function useFadeIn(options = {}) {
  const { duration = 0.5, delay = 0, y = 30 } = options;
  const ref = useRef(null);

  useGSAP(() => {
    gsap.from(ref.current, {
      opacity: 0,
      y,
      duration,
      delay,
      ease: 'power2.out'
    });
  });

  return ref;
}

// Usage
function Card() {
  const cardRef = useFadeIn({ delay: 0.2 });
  return Card content;
}

useHoverAnimation Hook

function useHoverAnimation(enterAnimation, leaveAnimation) {
  const ref = useRef(null);
  const { contextSafe } = useGSAP({ scope: ref });

  const onEnter = contextSafe(() => enterAnimation(ref.current));
  const onLeave = contextSafe(() => leaveAnimation(ref.current));

  return { ref, onMouseEnter: onEnter, onMouseLeave: onLeave };
}

// Usage
function Button() {
  const hoverProps = useHoverAnimation(
    (el) => gsap.to(el, { scale: 1.05, duration: 0.2 }),
    (el) => gsap.to(el, { scale: 1, duration: 0.2 })
  );

  return Hover me;
}

Temporal Collapse Patterns

Animated Countdown Digit

function CountdownDigit({ value, label }) {
  const digitRef = useRef(null);
  const prevValue = useRef(value);

  useGSAP(() => {
    if (prevValue.current !== value) {
      gsap.timeline()
        .to(digitRef.current, {
          rotationX: -90,
          opacity: 0,
          duration: 0.25,
          ease: 'power2.in'
        })
        .call(() => {
          digitRef.current.textContent = value;
          prevValue.current = value;
        })
        .fromTo(digitRef.current,
          { rotationX: 90, opacity: 0 },
          { rotationX: 0, opacity: 1, duration: 0.25, ease: 'power2.out' }
        );
    }
  }, { dependencies: [value] });

  return (
    
      {value}
      {label}
    
  );
}

Cosmic Pulse Effect

function CosmicPulse({ children, color = '#00F5FF' }) {
  const containerRef = useRef(null);

  useGSAP(() => {
    gsap.to(containerRef.current, {
      boxShadow: `0 0 30px ${color}, 0 0 60px ${color}`,
      duration: 1,
      repeat: -1,
      yoyo: true,
      ease: 'sine.inOut'
    });
  }, { scope: containerRef });

  return {children};
}

Performance Tips

// 1. Use will-change for heavy animations
gsap.set('.animated', { willChange: 'transform' });

// 2. Batch similar animations
useGSAP(() => {
  gsap.to('.item', { opacity: 1, stagger: 0.1 }); // Single tween
  // Not: items.forEach(item => gsap.to(item, ...)) // Multiple tweens
});

// 3. Use refs over selectors for frequently animated elements
const boxRef = useRef(null);
gsap.to(boxRef.current, { x: 100 }); // Faster

// 4. Kill animations on rapid state changes
const tweenRef = useRef(null);
useEffect(() => {
  tweenRef.current?.kill();
  tweenRef.current = gsap.to(...);
}, [dependency]);

Reference

  • See gsap-fundamentals for animation basics
  • See gsap-sequencing for timeline composition
  • See gsap-scrolltrigger for scroll-based animations

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.