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

.box, .circle {
  margin: 10px auto;
}
import React, { 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, timeline, index }) {
  const el = useRef();
  
  useGSAP(() => {
    // add 'left 100px' animation to timeline
    timeline && timeline.to(el.current, { 
      x: -100 
    }, index * 0.1);
    
  }, [timeline, index]);

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

function Circle({ children, timeline, index, rotation }) {
  const el = useRef();

  useGSAP(() => {
    // add 'right 100px, rotate 360deg' animation to timeline
    timeline && timeline.to(el.current, { 
      rotation: rotation, 
      x: 100 
    }, index * 0.1);
    
  }, [timeline, rotation, index]);

  return <div className="circle gradient-blue" ref={el}>{children}</div>;
}

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

  const { contextSafe } = useGSAP(() => {
    const tl = gsap.timeline();
    setTl(tl);
  });

  const toggleTimeline = contextSafe(() => {
    tl.reversed(!tl.reversed());
  })

  return (
      <div className="app">
        <button onClick={toggleTimeline}>Toggle</button>
        <Box timeline={tl} index={0}>Box</Box>
        <Circle timeline={tl} rotation={360} index={1}>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