Jump to content

Justin N

React + GSAP Working with animations in components

Go to solution Solved by GreenSock,

Recommended Posts



I know it can be frustrating to continuously direct people to read the documentation and look at the examples, so I want to say that I have been working on this issue for over a week. I have tried rebuilding this numerous times following the guides.  Now while coding the CodePen example for this post, some of the issues I had worked!  So, instead of saying, it's not doing what I want, I want to ask for some code review please.


What am I trying to do? 

- I have an app that has various react components being added and removed.

- The components have animations

- I would like to do two things in terms of animations in these: 

  - Create an animation on load and be able to navigate the timeline of tweens added within the useLayoutEffect 

  - Add to the timeline depending on actions 


- Is this the correct general approach to adding animations in components that appear on a SPA ?

- The reverse appears to work for both the tween in the useLayoutEffect and the tween added via button click. 

- Playing after the reverse appears to work.

- Is it correct to put the tl var in the array [] for useLayoutEffect? I did this because without it, I get a warning that it should be there.


CodeSandbox Example

Link to comment
Share on other sites

  • Solution

I don't really have time to do a full code review for you (that's not generally what these forums are for, but we're happy to answer any GSAP-specific questions) but here are some quick comments: 

  1. You did a nice job of using gsap.context() inside your useLayoutEffect() but you forgot the most critical piece - cleanup! :) You should return a cleanup function, like return () => ctx.revert();
  2. For the scope of the gsap.context(), you used document.getElementById("#secondCompRoot") but I think that's poor practice because imagine what would happen if you added multiple instances of this component to an app - you'd end up with multiple elements with the same ID in the DOM, and your selector would always just return the first one. You should create a ref instead so that it's absolutely unique.
  3. I'm not much of a React guy, but it looks wrong (to me) to create your timeline OUTSIDE of a useLayoutEffect() or useEffect() - doesn't that mean it'll recreate an entirely new instance on every render? So things may start getting added to a new timeline (and the old ones are gone)?  My guess is that it'd be much better to create it inside the useLayoutEffect(), in the gsap.context().
  4. Instead of creating loose functions that create tweens interactively (thus they're NOT included in the gsap.context() for easy cleanup), I'd add named functions to the context that you reference instead so that any animations you create inside there are captured in the context for easy cleanup. Plus you get the added benefit of selector scoping.

Here's a fork:



I hope that helps!

  • Like 1
  • Thanks 1
Link to comment
Share on other sites



As Jack points out, is better to create your GSAP instances inside the layout effect and inside GSAP Context.


Another option is to create the a ref that will hold the GSAP timeline and then create the timeline inside GSAP Context. Something like this:

const tl = useRef(); // tl.current is undefined at this point

useLayoutEffect(() => {
  const ctx = gsap.context(() => {
    tl.current = gsap.timeline();
  }, scope);
  return () => ctx.revert();
}, []);

const playTween = () => {

const reverseTween = () => {

Of course this is missing the feature to add new instances to the timeline, so in that scenario is better to follow the pattern Jack posted in the example he created, but is worth mentioning how to approach a scenario when you need a reusable GSAP instance (Tween/Timeline) in your app/component.


As for adding tl to your dependencies array, that's a tricky one actually. Normally I don't add them because I either use a ref or create them inside the layout effect hook. If for some reason you still create them outside the hook eslint will ask for it, but the real question is if the code inside the effect hook needs to run again once that is updated and normally the answer to that is no. But as mentioned before, just avoid creating the GSAP instances in a constant or variable at the root scope of your component and create them in the layout effect hook or a ref if you need to access them somewhere else in your component.


Happy Tweening!

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Thank you both for your advice.  


You're absolutely right about the tl creating a new instance on every render and it doesn't make that much sense. Just a note, I think most of the examples in the docs for GSAP+React shows the tl being created outside the useLayoutEffect.  It may not crash things but there are definitely unexpected side effects / behaviour. 


I find the useRef option handy especially when you might want to access the timeline beyond the component mount / dismount. 


Cheers !

Link to comment
Share on other sites

38 minutes ago, Justin N said:

Just a note, I think most of the examples in the docs for GSAP+React shows the tl being created outside the useLayoutEffect.



Can you point the specific places in the docs where you've seen that pattern? I went through the official articles we have for our users and can't find what you mentioned.


Happy Tweening!

Link to comment
Share on other sites

47 minutes ago, Rodrigo said:

Can you point the specific places in the docs where you've seen that pattern? I went through the official articles we have for our users and can't find what you mentioned.

Hello Rodrigo,

It's subtle but there in (https://greensock.com/react) .  If you look on the GSAP+React page, you'll see examples in which useLayoutEffect is applied but there is not creation of the tl inside of those. There is one exception on that page in which they show that it is better to create the timeline in useLayoutEffect and to use a useRef outside.

Since the GSAP + React page (https://greensock.com/react) is likely one of the first pages a new user would look for if working in React, do you think it would be better to explicitly show the creation of the timeline in the useLayoutEffect hook ?   Below is a snag of one of the examples from that page:






Link to comment
Share on other sites



I'm a bit confused by your latest post TBH. In every example in both guides we use a GSAP Context instance inside a useLayoutEffect hook. That's basically the pattern we promote here in the forums and in every codepen example we have in both articles and the starter templates in Stackblitz:




There might not be Timeline instances on most of the examples in the articles, but that's just because a Timeline instance is not needed, but every GSAP Instance is added inside a GSAP Context that resides and is executed on the useLayoutEffect hook. So the hook is exactly where/when the GSAP instances are being created.


If this is not clear in someway for you, please let us know so we can fix any possible issues or help you clear any confusion in this regard.


Happy Tweening!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.