bbeierle12/skill-mcp-claude2 files

Gsap React

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.

Specification
Skill ID
bbeierle12/skill-mcp-claude/gsap-react
Publisher
bbeierle12
Repository
skill-mcp-claude
Installs
0
Files
2
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

bbeierle12/skill-mcp-claude/gsap-reactInstalls these files
  • SKILL.md
  • _meta.json

What this skill tells the agent

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 (
    <div ref={containerRef}>
      <div className="box">Animated</div>
    </div>
  );
}

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 (
    <div ref={container}>
      <div className="item">Item 1</div>
      <div className="item">Item 2</div>
      <div className="item">Item 3</div>
    </div>
  );
}

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 (
    <div ref={container}>
      <div className="drawer">Content</div>
    </div>
  );
}

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 (
    <div ref={container}>
      <div className="box" onClick={handleClick}>Click me</div>
    </div>
  );
}

Ref Patterns

Single Element Ref

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

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

  return <div ref={boxRef}>Box</div>;
}

Multiple Element Refs

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

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

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

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 (
    <div>
      {items.map(item => (
        <div
          key={item.id}
          ref={el => {
            if (el) itemsRef.current.set(item.id, el);
            else itemsRef.current.delete(item.id);
          }}
        >
          {item.name}
        </div>
      ))}
    </div>
  );
}

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 (
    <div ref={container}>
      <div
        className="box"
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      >
        Hover me
      </div>
    </div>
  );
}

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 <div ref={boxRef} onClick={animateBox}>Click</div>;
}

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 (
    <div ref={container}>
      <div className="box">Animated</div>
      <button onClick={play}>Play</button>
      <button onClick={reverse}>Reverse</button>
      <button onClick={restart}>Restart</button>
    </div>
  );
}

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 (
    <div ref={container}>
      <div className="element">Controlled</div>
    </div>
  );
}

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 (
    <div ref={container}>
      <div className="section">Scroll to reveal</div>
    </div>
  );
}

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() {

Use this skill in a project

Open any RiverX project, search the Skills panel for bbeierle12/skill-mcp-claude/gsap-react, and install it in one click.

Use this skill