Jump to content

Search the Community

Showing results for tags 'react'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge


There are no results to display.

Find results in...

Find results that contain...

Date Created

  • Start


Last Updated

  • Start


Filter by number of...


  • Start



Personal Website



Company Website



  1. Hello! I wanted to know in terms of performance if there's any difference between using multiple useRef to select the elements of a react component and using the new feature gsap.context. Second if it's posibble I'd like to know what is it going on in the shadows, how is that the library makes sure that the strings I pass which are inside the parent ref are getting targeted. Is there somewhere I could read about it?
  2. Hola comunidad, hice un ejemplo de un elemento pin con gsap y reaccion, creo que lo hice bien, me gustaria saber si hay alguna forma de optimizar el codigo o si esta bien planeado, intente hacer un arreglo con utils para lograr el efecto del texto pero no lo conseguí, solo pude hacerlo usando cada componente individualmente, ¿es posible lograr ese efecto con "gsap.utils.toArray"? Caja de arena: https://codesandbox.io/s/gsap-react-pin-scroll-24c7fc?file=/src/App.js
  3. I'm a beginner at GSAP. I have a complex SVG which runs perfectly in HTML. I'm trying to convert it into React by using GSAP. How can I convert the HTML SVG in react? Here's the link to HTML SVG: https://codesandbox.io/s/demo-svg-html-esf3dc?file=/index.html While you put hover over the circle it is animated. Here's the Link to my React App: https://codesandbox.io/s/framer-motion-svg----3333-zcvdk1?file=/src/components/MainSVG.js I try to put all curves parents' id in the motion path. I got an error. Now as you can see I just put only 1 path id in the motion path and all works like a mess. Here's a JS function but I don't know where and how to add that in react. Maybe if I add that to my code it will work. const existElementId = (e) => { return document.getElementById(e) } existElementId("circle" + 1) for (let i = 1; null != existElementId("circle" + i); i++) { console.log(existElementId("circle" + i)) let tl = gsap.timeline({ repeat: -1 }); tl.to("#dot" + i, { duration: document.querySelectorAll("#curve" + i + " path")[0].getTotalLength() / 200, ease: "none", motionPath: { path: "#curve" + i + " path", align: "#curve" + i + " path", alignOrigin: [0.5, 0.5], } }); tl.pause() existElementId("circle" + i).onmouseover = () => { tl.play() } existElementId("circle" + i).onmouseleave = () => { tl.pause().time(0) } } I'm expecting to get any solution/idea to make it like the index.html file.
  4. Is it possible to create that by code using GSAP?
  5. Hi, im tying to use scrollProxy with locomotive like in the docs with Nextjs. Locomotive works fine but scrollTrigger its always at the initial position. It seams that the scrollTop inside the scrollerPoxy its not returning any value. I made a codesandbox to show the case: Scroll component: https://codesandbox.io/s/nextjs-gsap-locomotive-qq11t?file=/src/components/scroll.jsx Live demo: https://qq11t.sse.codesandbox.io/ I'm looking for a good way to implement a smooth-scroll in nextjs so if any have an alternative to locomotive and works with gsap it would help any way. Thx, for the support
  6. React and GSAP can be a powerful combination, as evidenced by many of the sites in our showcase. GSAP is a framework-agnostic animation library, you can write the same GSAP code in React, Vue, Angular or whichever framework you chose, the core principles won't change. There are some React-specific tips and techniques that will make your life easier though, let's take a look... This is not a tutorial, so feel free to dip in and out as you learn. Why GSAP? GSAP is so popular because it delivers unprecedented control and flexibility, and that's exactly what award-winning developers want. You can reach for GSAP to animate anything — from simple DOM transitions to SVG, Three.js, canvas or WebGL, even generic JavaScript objects — your imagination is the limit. Most importantly, you can rely on us. We obsess about performance, optimizations and browser compatibility so that you can focus on the fun stuff. We've actively maintained and refined our tools for over a decade. If you get stuck, our active and friendly forum community is there to help. A basic understanding of GSAP and React is assumed. 🤷🏼‍♀️ If you're just getting going with React, this tutorial from the React team is a great place to start. 🤔 Need a GSAP refresher? Take a break and read the getting started guide which covers tweens and timelines. 🥳 Feeling confident? Skip straight to part 2 - GSAP + React, advanced animation techniques. Online playgrounds Get started quickly by forking one of these starter templates: CodePen CodeSandbox CodeSandbox + Bonus Plugins Create a new React App If you prefer to work locally, Create React App provides a comfortable setup for experimenting with React and GSAP. To create a project, run: npx create-react-app gsap-app cd gsap-app npm start Once the project is set up we can install GSAP through npm, npm i gsap npm start Then import it into our app. import React from "react"; import { gsap } from "gsap"; export default function App() { return ( <div className="app"> <div className="box">Hello</div> </div> ); } More detailed information about getting started with React Additional GSAP installation documentation Animating on interaction Let's start with a common challenge - animating on a user interaction. This is pretty straightforward with React. We can hook into callbacks to fire off animations on certain events like click or hover. In this demo the box is scaling up onMouseEnter, and down onMouseLeave. See the Pen React Tutorial 1d by GreenSock (@GreenSock) on CodePen. But what if we want an animation to fire after the component mounts, without a user triggered callback? Triggering animation on mount - useLayoutEffect() The useLayoutEffect() hook runs immediately AFTER React has performed all DOM mutations. It's a very handy hook for animation because it ensures that your elements are rendered and ready to be animated. Here's the general structure: const comp = useRef(); // create a ref for the root level element (we'll use it later) useLayoutEffect(() => { // -- ANIMATION CODE HERE -- return () => { // cleanup code (optional) } }, []); // <- empty dependency Array so it doesn't re-run on every render! Don't forget that empty dependency Array! If you omit that, React will re-run the useLayoutEffect() on every render. Targeting elements with Refs In order to animate, we need to tell GSAP which elements we want to target. The React way to access DOM nodes is by using Refs. Refs are a safe, reliable reference to a particular DOM node. const boxRef = useRef(); useLayoutEffect(() => { // Refs allow you to access DOM nodes console.log(boxRef) // { current: div.box } // then we can animate them like so... gsap.to(boxRef.current, { rotation: "+=360" }); }); return ( <div className="App"> <div className="box" ref={boxRef}>Hello</div> </div> ); However - animation often involves targeting many DOM elements. If we wanted to stagger 10 different elements we'd have to create a Ref for each DOM node. This can quickly get repetitive and messy. So how can we leverage the flexibility of selector text with the security of Refs? Enter gsap.context(). gsap.context() is your best friend! gsap.context() provides two incredibly useful features for React developers, the option of using scoped selectors and more critically - animation cleanup. GSAP Context is different than React Context. Scoped Selectors We can pass a Ref into context to specify a scope. All selector text (like ".my-class") used in GSAP-related code inside that context will be scoped accordingly, meaning it'll only select descendants of the Ref. No need to create a Ref for every element! Here's the structure: const comp = useRef(); // create a ref for the root level element (for scoping) const circle = useRef(); useLayoutEffect(() => { // create our context. This function is invoked immediately and all GSAP animations and ScrollTriggers created during the execution of this function get recorded so we can revert() them later (cleanup) let ctx = gsap.context(() => { // Our animations can use selector text like ".box" // this will only select '.box' elements that are children of the component gsap.to(".box", {...}); // or we can use refs gsap.to(circle.current, { rotation: 360 }); }, comp); // <- IMPORTANT! Scopes selector text return () => ctx.revert(); // cleanup }, []); // <- empty dependency Array so it doesn't re-run on every render // ... In this example, React will first render the box and circle elements to the DOM, then GSAP will rotate them 360deg. When this component un-mounts, the animations are cleaned up using ctx.revert(). See the Pen React & GSAP Starter Template by GreenSock (@GreenSock) on CodePen. 🧠 deep dive... Refs or scoped selectors? show more... Targeting elements by using selector text like ".my-class" in your GSAP-related code is much easier than creating a ref for each and every element that you want to animate - that’s why we typically recommend using scoped selectors in a gsap.context(). An important exception to note is if you’re going to be nesting components and want to prevent against your selectors grabbing elements in child components. In this example we've got two elements animating in the main App. A box targeted with a scoped class selector, and a circle targeted with a Ref. We've also nested another component inside our app. This nested element also has child with a class name of '.box'. You can see that the nested box element is also being targeted by the animation in the App's effect, whereas the nested circle, which was targeted with a Ref isn't inheriting the animation. See the Pen React & GSAP Starter Template by GreenSock (@GreenSock) on CodePen. Cleaning Up useLayoutEffect() provides us with a cleanup function that we can use to kill animations. Proper animation cleanup is crucial to avoid unexpected behaviour with React 18's strict mode. This pattern follows React's best practices. gsap.context makes cleanup nice and simple, all GSAP animations and ScrollTriggers created within the function get collected up so that you can easily revert() ALL of them at once. We can also use this cleanup function to kill anything else that could cause a memory leak, like an event listener. useLayoutEffect(() => { const ctx = gsap.context(() => { const animation1 = gsap.to(".box1", { rotation: "+=360" }); const animation2 = gsap.to(".box2", { scrollTrigger: { //... } }); }, el); const onMove = () => { //... }; window.addEventListener("pointermove", onMove); // cleanup function will be called when component is removed return () => { ctx.revert(); // animation cleanup!! window.removeEventListener("pointermove", onMove); // Remove the event listener }; }, []); gsap.matchMedia() uses gsap.context() under the hood, so you can just call revert() on your matchMedia instance instead for cleanup (no need to combine them). Reusing components Within a component based system, you may need more granular control over the elements you're targeting. You can pass props down to children to adjust class names or data atrributes and target specific elements. React advises to use classes purely for styling and data attributes to target elements for JS functionality like animations. In this article we'll be using classes as they're more commonly understood. See the Pen Forwarding refs by GreenSock (@GreenSock) on CodePen. Creating and controlling timelines Up until now we've just used refs to store references to DOM elements, but they're not just for elements. Refs exist outside of the render loop - so they can be used to store any value that you would like to persist for the life of a component. In order to avoid creating a new timeline on every render, it's important to create the timeline inside an effect and store it in a ref. function App() { const el = useRef(); const tl = useRef(); useLayoutEffect(() => { const ctx = gsap.context(() => { tl.current = gsap .timeline() .to(".box", { rotate: 360 }) .to(".circle", { x: 100 }); }, el); }, []); return ( <div className="app" ref={el}> <Box>Box</Box> <Circle>Circle</Circle> </div> ); } This will also allow us to access the timeline in a different useEffect() and toggle the timeline direction. See the Pen React Tutorial 2f by GreenSock (@GreenSock) on CodePen. Controlling when React creates our animation. If we don't pass a dependency Array to useLayoutEffect(), it is invoked after the first render and after every update. So every time our component’s state changes, it will cause a re-render, which will run our effect again. Typically that's wasteful and can create conflicts. We can control when useLayoutEffect should run by passing in an Array of dependencies. To only run once after the first render, we pass in an empty Array, like []. You can read more about reactive dependencies here. // only runs after first render useLayoutEffect(() => { const ctx = gsap.context(() => { gsap.to(".box-1", { rotation: "+=360" }); }, el); }, []); // runs after first render and every time `someProp` changes useLayoutEffect(() => { const ctx = gsap.context(() => { gsap.to(".box-2", { rotation: "+=360" }); }, el); }, [someProp]); // runs after every render useLayoutEffect(() => { const ctx = gsap.context(() => { gsap.to(".box-3", { rotation: "+=360" }); }, el); }); See the Pen React Tutorial 1b by GreenSock (@GreenSock) on CodePen. Reacting to changes in state Now that we know how to control when an effect fires, we can use this pattern to respond to changes in our component. This is especially useful when passing down props. function Box({ children, endX }) { const boxRef = useRef(); // run when `endX` changes useLayoutEffect(() => { const ctx = gsap.context(() => { gsap.to(boxRef.current, { x: endX }); }); return () => ctx.revert(); }, [endX]); return ( <div className="box" ref={boxRef}> {children} </div> ); } See the Pen React Tutorial 1c by GreenSock (@GreenSock) on CodePen. We hope this article was helpful - If you have any feedback please leave us a comment below so we can smooth out the learning curve for future animators! Read the next article.
  7. Hi there! I'm really in love with the new GSAP Context! It's really cool with working with React! When i add timelines with `context.add()` I get a typescript error because the method does not exist as a property of the context object. So the question is: How do I declare a type in typescript (and GSAP) that I intend to add a specific timeline method to the GSAP context? Here's an example: // Borrowing this context hook from the docs @see: https://greensock.com/react-advanced#useGsapContext export function useGsapContext<T extends HTMLElement = HTMLDivElement>( scope: RefObject<T>, // eslint-disable-next-line @typescript-eslint/no-empty-function context: gsap.ContextFunc = () => {}, ) { // eslint-disable-next-line react-hooks/exhaustive-deps const ctx = useMemo(() => gsap.context(context, scope), [scope]); return ctx; } I see that the return types for context are: interface Context { [key: string]: any; selector?: Function; isReverted: boolean; conditions?: Conditions; queries?: object; add(methodName: string, func: Function, scope?: Element | string | object): Function; add(func: Function, scope?: Element | string | object): void; ignore(func: Function): void; kill(revert?: boolean): void; revert(config?: object): void; clear(): void; } Now I'm adding a method to the context like so: // Init the gsap context const ctx = useGsapContext(wrapperRef); // Adds the timeline method to the context. This useEffect runs only once after initial render useEffectOnce(() => { ctx.add("newTab", (newIdentifier: string, oldIdentifier: string) => { const { current: wrapperEl } = wrapperRef; if (!wrapperEl) return; const tl = gsap.timeline(); if (oldIdentifier) { tl.to(`[data-tab="${oldIdentifier}"]`, { duration: 0.5, scale: 0.9, autoAlpha: 0, }); } tl.fromTo( `[data-tab="${newIdentifier}"]`, { scale: 1.2, autoAlpha: 0, }, { scale: 1, autoAlpha: 1, }, ); }); }); // on state update - uses the timeline we added to the context useUpdateEffect(() => { // Using the method added to context (this works!) But typescript complains this method doesn't exist if ("newTab" in ctx && typeof ctx["newTab"] === "function") { ctx.newTab(activeValue.active, activeValue.prev); } }, [activeValue]); So the timeline works as expected which is great... but I get the following error which is expected because typescript doesn't know there is a "newTab" property on the context object
  8. Hello, First of all, I want to say that GSAP has been a fantastic product that has solved many problems for me. I really enjoy using it for my projects! Lately, I started to use scrollTrigger with React and I noticed a strange behaviour. In my minimal codesandbox example, I designed it so that the scrollTrigger is only allow to slide the "door" div to the right during onEnter on the first time only. I am using React state in "iterCount" to prevent the door div from triggering any subsequent time. However, when I console.log the iterCount state, it appears that the scrollTrigger never received the updated iterCount after it was incremented by the onComplete event. Meanwhile, the iteration count is correctly incrementing in the React App itself in the counter at the top of the page. What is missing here? https://codesandbox.a/s/gsap-updating-state-hooks-sliding-door-xucoo0
  9. When the page is reloaded, the animation is not performed. What is the problem? const array = [ { title: "Ref Element 1" }, { title: "Ref Element 2" }, { title: "Ref Element 3" } ]; export function Slider() { const titleH1Refs = useRef([]); titleH1Refs.current = []; //checking for an existing element in an array const addToRefsTitleH1 = el => { if (el && !titleH1Refs.current.includes(el)) { titleH1Refs.current.push(el); } }; //perform animation for all array elements useLayoutEffect(() => { titleH1Refs.current.forEach((element) => { gsap.from(element, { opacity: 0, y: 20, ease: Expo.easeInOut }) }) }, [titleH1Refs.current]); return ( <section className="container"> {array.map((element) => ( <div className="element" ref={addToRefs}>{element.title}</div> ))} </section> ); }
  10. Hi, I wanted to create a vertical/horizontal scroll but I've meet a problem where when using react 18, project just dont work. Could someone help me fix this issue so I can use react 18? https://codesandbox.io/s/test-forked-i9ng90?file=/src/App.js
  11. Hi! I was trying to achieve a similar effect to this codepen (found it in the gsap showcase) , but without all the complications of the loop. Most precisely, I was trying to achieve that instant snap effect, but without restarting when the array ends. Something exactly like this. I spent several hours yesterday without catching it, I would really apprec.iate any start point to work arround. Thanks in advance
  12. // MySplitText.tsx const ref = useRef<HTMLDivElement>(null) const splitText = useRef<SplitText>() useEffect(() => { let ctx = gsap.context(() => { splitText.current = new SplitText('.text',{ type: 'chars' }) },ref) },[]) <div ref={ref}> <p className="text">My Text</p> </div> /** * Root component- App.tsx */ <div> <MySpliteText/> <p className="text">My Text at root component</p> // <-- this is got selected </div> Does someone also experience this scenario? The selector inside the context is leaking outside its scope, or is there something wrong with instantiation of the `SplitText` plugin? I have to pass it to a `ref` so I can use it on a callback animation.
  13. I keep getting this occasional issue on page refresh - the local development environment the animations are not getting triggered on the component is on view - and messages on the console Could my code implementation on React be causing the issue? https://codesandbox.io/s/react-gsap-2lmcof?file=/src/data.js I was not able to reproduce the issue on codesand box - but it's a simplifcy version how i'm implenenting Gsap on react / next.js app. I'm new to width GSAP but I'm super excited to build more animations with it. Thank you
  14. Hi everyone, Im trying to use ScrollTrigger with Next.js. I just coppied https://codesandbox.io/s/gsap-scrollsmoother-next-js-starter-0h67eh?file=/pages/index.js but my example not work like that. boxC moving a little bit between start and end position, after it jumps 150-200px and it moves again with scrolling. Thanks Screen Recording 2022-11-10 at 21.45.18.mov
  15. Hi, I'm learning how to use scrolltrigger in react but, for some reason, the code works weird as you can see in the video. If I use the same code on codepen everything works as it should. I searched for a solution on google and here on the forum but couldn't find anything, please help me to understand import { useEffect } from "react"; import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import "./App.css"; gsap.registerPlugin(ScrollTrigger); function App() { useEffect(() => { const tl = gsap.timeline({ scrollTrigger: { markers: true, trigger: ".space2", start: "top center", scrub: 2, toggleActions: "restart none none none", pin: ".box", }, }); tl.to(".box", { x: 1000, duration: 8, }); }, []); return ( <div className="App"> <div class="space1"></div> <div class="wrap"> <div class="box"></div> </div> <div class="space2"></div> </div> ); } gsap.mp4
  16. import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { useLayoutEffect } from "react"; gsap.registerPlugin(ScrollTrigger); function Home() { useLayoutEffect(() => { let tl = gsap.timeline({ defaults: { immediateRender: false, }, }); tl.to(".animaition-112", { opacity: 1, y: 0, scrollTrigger: { trigger: ".animaition-112", start: "top center", end: "center 30%", markers: true, }, }); return () => { tl.kill(); }; }, []); return <></> } why scrolltrigger animation is not working and triggering mechanism is worked and there is no transition. if i leave timeline and use gsap.to() that will work what can i do there. help please.
  17. Zergor

    Drawer Examples

    Hey Guys! I'm creating a website with React that has a menu button that opens a drawer for mobile/tablet. I'm using the MUi drawer (temporary drawer, left) and it *almost* works great. In Chrome for IOS, there's a bug with css transitions in which if you switch tabs and come back, the animation gets worse and after three times, the animation stops instantly. I came across gsap/motion/react spring, all of which work great with the chrome iOS bug as they don't use css transitions. Problem is my JS skills are weak (slowly improving) and I've no idea how to recreate the MUI drawer from scratch using GSAP. I get by with modifying existing examples. A bit of a long shot but does anyone have any recent codepens that function similar to the MUI example? - Click on the menu, opens drawer. - Click on a close menu button, closes drawer - Click on the remaining body, closes drawer https://mui.com/material-ui/react-drawer/ Thanks in advance if anyone has any examples they can share if they've made any in recent months.
  18. Hello everyone, I tried to search the forum but I didn't find any project regarding horizontal scroll done in NextJs, could someone kindly point me to it? Thank you very much and have a nice day
  19. https://codesandbox.io/p/sandbox/gsap-and-fixed-positioning-test-forked-92gisp?selection=[{"endColumn"%3A32%2C"endLineNumber"%3A21%2C"startColumn"%3A32%2C"startLineNumber"%3A21}]&file=%2Fpages%2Findex.tsx&workspace=%7B%22activeFileId%22%3A%22cl8skx9ay000hlpge4cua49bz%22%2C%22openFiles%22%3A%5B%5D%2C%22sidebarPanel%22%3A%22EXPLORER%22%2C%22gitSidebarPanel%22%3A%22COMMIT%22%2C%22sidekickItems%22%3A%5B%7B%22type%22%3A%22PREVIEW%22%2C%22taskId%22%3A%22dev%22%2C%22port%22%3A3000%2C%22key%22%3A%22cl9do5h5900gq496uufxj1u33%22%2C%22isMinimized%22%3Afalse%7D%5D%7D Hi, I need to explore using ScrollTrigger.start and ScrollTrigger.end properties, but they don't log the correct values; however, if I log the entire ScrollTrigger object, I see the correct start and end values. How can I solve this to get the correct values?
  20. code sandbox link (I was messing with this file and forgot to fork it, so I apologize, the file is now as it should be) In my little basic nextjs demo here (link above), I have set up an idea for a page I have where it would use a fixed position layout as you see here. I have three questions that are hopefully not too long: 1. Is this a good way to set up such a layout with gsap (using stacked, standard flow elements, in this case, the yellow and green lines in the middle of the page (which would be hidden in the actual site) to trigger the scrolling animation) or do you think there is a better way to achieve this? 2. If I scroll any amount of distance and hit refresh, the page doesn't reload from the beginning it just reloads and the scroll bar remains in place. What should I do to make it start back from scratch when I refresh the page? 3. I was trying out smooth-scroller, got it to work and it's very nice of course; however, If I have A setup like this in nextjs with these fixed elements which the smooth-scroller docs say to put outside the smooth content wrapper, I would have the main content in a wrapper (this wrapper would have smooth-scroller) then the fixed elements in their own parent div and then wrap all of that in say a react fragment or div because of course this is react and all components must only render one element. But when I do that smooth-scroller doesn't work anymore, So I'd love to see how smooth-scroller would get applied to this setup. Also, is there a way to make smooth-scroller work in code sandbox?
  21. hello everyone, i am having trouble with scrolltrigger pin method. i seem to cannot solved is at all , cannot figure out if its reacts problem or gsap need help pleaseeeeeeeee https://codesandbox.io/s/zen-fast-ogesxo?file=/src/App.js
  22. Hi there, New here (also new with gsap/react); I have made a side bar that slides out from the right side, I got the slide in animation to work but can't use .reverse() to reverse that animation. It immediately resets back to xPercent: 100 , instead of animating. Anyone here able to give me any pointers on how I should proceed? Small code snippet: const [menuOpen, setMenuOpen] = useState<boolean>(false); const [toggle, setToggle] = useState<boolean>(false); const handleToggle = () => { setToggle(!toggle); setMenuOpen(prev => !prev); }; const tl = gsap.timeline({ paused: true }); useEffect(() => { tl.fromTo('.hamburger__overlay', { xPercent: 100, duration: 1, }, { xPercent: 0, duration: 1, } ).reverse() tl.reversed(!toggle); }); (Dont mind the styling it's just a quick demo ) CodeSandbox link: https://codesandbox.io/s/sidebar-y45kf1
  23. Hello! I'm trying to get a React component to fade in and out smoothly. I don't understand what to do next. Help me please
  24. Hi, i tried to use, Gsap Flip to filter a grid of elements. The filter is inserted in the page in a fixed position so I can filter the grid from any position of the scroll. The strange thing that happens is that if I filter the grid with the scroll positioned at the bottom of it, the page will be scrolled up. I also tried to give a fixed height to the grid container, to avoid the bug, but the same thing happens. Is it a problem with Flip or am I using it wrong? https://codesandbox.io/s/shy-fire-3j0xc5
  25. Hi all, I am new to gsap and this library has been fantastic so far. I was looking to recreate the following effect where you click an image, and it flips to the corresponding page. https://dribbble.com/shots/17535054-Homepage-Animation-for-Melbourne-Wooden-Showroom After doing a bit of digging, I thought I could use the following demo below as a base to toggle between component states. https://codepen.io/GreenSock/pen/OJzRdBj To start, I thought it would be best to observe how the component would behave when used multiple times within another component, like a list. Here is where I'm at: https://codesandbox.io/s/gsap-flip-react-test-t3odk8?file=/src/App.js I expected there to be some problem with the states, but I'm not too sure why clicking on any of the three list items only triggers a flip from the last item in the list. It would be super helpful if someone could help me with this, or point me in any direction if this is not the ideal way to approach this. Please let me know if I can provide any additional information. Thank you in advance.