Working with GSAP in a Nuxt3 project, can sometimes lead to performance issues (I know I did!). This happens due to tweens lingering in the JavaScript memory causing weird lags and slow downs that drag the user experience.
Plus, since Nuxt is a server-side rendered (SSR) framework by default, it becomes rather cumbersome with the cluttering of your components with repetitive boilerplate.
So the dev experience isn’t any fun either.
The Problem
So typically, it seems that GSAP animations are not automatically cleaned up, which can cause them to accumulate and bog down your application's performance.
Add in the fact Nuxt is SSR and therefore will execute JavaScript on the server before it hits the client-side, which doesn't have access to the DOM. We need to constantly use the onMounted hook.
Here is a common setup:
import { gsap } from "gsap";
onMounted(() => {
//needs this hook since the script tag runs in SSR before the DOM is present.
gsap.from(".site-header", {
delay: "0.5",
duration: 0.5,
y: -50,
opacity: 0,
ease: "power4.out",
});
});
Introducing gsap.context()
I learnt about the gsap.context()
method, which offers a neat solution by encapsulating all animations within a given context, making it easy to manage and dispose of them efficiently when they are no longer needed.
Here’s how you can better manage animations with gsap.context()
, ensuring they don’t overstay their welcome,
Kill tweens after a time (Not Ideal):
import { gsap } from "gsap";
onMounted(() => {
let context = gsap.context(() => { //create context
gsap.from(".site-header", {
delay: "0.5",
duration: 0.5,
y: -50,
opacity: 0,
ease: "power4.out",
});
});
setTimeout(() => {
context.kill(); //kill the animation after a second
}, 1000);
});
Kill when Component is unmounted:
import { gsap } from "gsap";
let context; //create this outside the hook to be able to use it in the second hook
onMounted(() => {
context = gsap.context(() => {
gsap.from(".site-header", {
delay: "0.5",
duration: 0.5,
y: -50,
opacity: 0,
ease: "power4.out",
});
});
});
onUnmounted(() => {
context.kill();
});
Now that we've seen the headaches GSAP can cause in a Nuxt3 project. I’ll walk you through the best solution ive gathered to keep things smooth, simple and clean.
Lets dive!
Creating a GSAP Plugin for Nuxt
to streamline using GSAP in Nuxt3 we can create a plugin. This centralizes your GSAP logic, making it easier to maintain and reuse:
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
export default defineNuxtPlugin((nuxtApp) => {
if (process.client) {
gsap.registerPlugin(ScrollTrigger);
}
return { provide: { gsap: { gsap, ScrollTrigger } } };
//this allows you to use gsap and its plugins from a single object 'gsap' containing them at from useNuxtApp()
});
Make sure to add GSAP to the transpile array in your nuxt.config
for optimal performance across all browsers:
export default defineNuxtConfig({
build: {
transpile: ["gsap"],
},
...
}
The useGsap
Composable
We can further encapsulate your GSAP logic within a custom composable for cleaner, more manageable code. This composable will wrap your animation contexts within its lifecycle hooks—specifically using onMounted
for initialization and onUnmounted
for cleanup.
Here’s how:
export default function useGsap(animationFunction) {
// auto imported by nuxt
const { $gsap } = useNuxtApp();
// To ensure the animation function is called within the GSAP context
let context;
// This context will automatically collect all animations created within it
onMounted(() => {
context = $gsap.gsap.context(() => {
// Provide gsap and ScrollTrigger to the animation function
if (animationFunction) animationFunction(...Object.values($gsap));
});
});
// Automatically clean up when the component using this composable is unmounted
onUnmounted(() => {
// Kills all animations in the context, reverting their properties to the initial states
context.kill();
});
return {
...$gsap, // allows you to destructure from the composble if needed
};
}
Using the Composable
useGsap((gsap) => { //pass the plugins you need (gsap, scrollTrigger, ...)
gsap.from(".site-header", {
delay: "0.5",
duration: 0.5,
y: -50,
opacity: 0,
ease: "power4.out",
}
});
Conclusion
Integrating GSAP into Nuxt3 efficiently can be challenging, but with the setup we've discussed, you can reduce complexity and enhance performance.
Encapsulating your animations in a custom composable, you ensure cleaner, more maintainable code and a smoother dev and user experience.
Check out this StackBlitz to see the composable in action and get a feel for how it can clean up your Nuxt3 projects.
Further reading:
Understanding GSAP Context: Master the nuances of animation context management with GSAP.
Using GSAP in Various Frameworks: Learn how GSAP integrates with other popular frameworks.
Nuxt3 Transpilation: Get insights into optimizing your Nuxt3 projects for production.
useGSAP for React: Discover how to implement similar animation strategies in React applications.
Nice! Area you going to make an official package/plugin for it and get it up on Github/NPM?