<div id="root" class="panel center"></div>
.app {
  padding: 10px;
}

.box, .circle {
  margin: 10px auto;
}
import React, {
  useCallback,
  useEffect,
  useLayoutEffect,
  useRef,
  useState
} from "https://esm.sh/react@18.3.1";
import ReactDOM from "https://esm.sh/react-dom@18.3.1";

import gsap from "https://esm.sh/gsap";
import { useGSAP } from "https://esm.sh/@gsap/react?deps=react@18.3.1";

console.clear();

function Box({ children, addAnimation, index }) {
  const el = useRef();

  useGSAP(() => {
    console.log("Box effect");
    const animation = gsap.to(el.current, { x: -200 });
    addAnimation(animation, index);
  }, [addAnimation, index]);

  return (
    <div className="box gradient-green" ref={el}>
      {children}
    </div>
  );
}

function Circle({ children, addAnimation, index, rotation }) {
  const el = useRef();
  
  useGSAP(() => {
    console.log("Circle effect");
    const animation = gsap.to(el.current, { rotate: rotation, x: 200 });
    addAnimation(animation, index);
  }, [addAnimation, index]);
  
  return <div className="circle gradient-blue" ref={el}>{children}</div>;
}

function App() {
  const [tl, setTl] = useState();

  const { contextSafe} = useGSAP(() => {
    console.log("App effect (create timeline)");
    const tl = gsap.timeline();
    setTl(tl);
  });

  const addAnimation = useCallback((animation, index) => {
     tl && tl.add(animation, index * 0.1);
  },[tl]);

  const toggleTimeline = contextSafe(() => {
    console.log("toggle")
    tl && tl.reversed(!tl.reversed())
  });
  
  return (
    <div className="app">
      <button onClick={toggleTimeline}>Toggle</button>
      <Box addAnimation={addAnimation} index={0}>Box</Box>
      <Circle addAnimation={addAnimation} index={1} rotation="360">Circle</Circle>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
View Compiled

External CSS

  1. https://codepen.io/GreenSock/pen/xxmzBrw.css

External JavaScript

  1. https://codepen.io/GreenSock/pen/NWoLXRG.js