# Gsap Frameworks

> |

- **Type:** Skill
- **Install:** `agentstack add skill-nexu-io-open-design-gsap-frameworks`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [nexu-io](https://agentstack.voostack.com/s/nexu-io)
- **Installs:** 0
- **Category:** [Content & Media](https://agentstack.voostack.com/c/content-and-media)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [nexu-io](https://github.com/nexu-io)
- **Source:** https://github.com/nexu-io/open-design/tree/main/skills/gsap-frameworks
- **Website:** https://open-design.ai

## Install

```sh
agentstack add skill-nexu-io-open-design-gsap-frameworks
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# GSAP with Vue, Svelte, and Other Frameworks

> Curated from GreenSock's official GSAP skills: https://github.com/greensock/gsap-skills

## When to Use This Skill

Apply when writing or reviewing GSAP code in Vue (or Nuxt), Svelte (or SvelteKit), or other component frameworks that use a lifecycle (mounted/unmounted). For **React** specifically, use **gsap-react** (useGSAP hook, gsap.context()).

**Related skills:** For tweens and timelines use **gsap-core** and **gsap-timeline**; for scroll-based animation use **gsap-scrolltrigger**; for React use **gsap-react**.

## Principles (All Frameworks)

- **Create** tweens and ScrollTriggers **after** the component’s DOM is available (e.g. onMounted, onMount).
- **Kill or revert** them in the **unmount** (or equivalent) cleanup so nothing runs on detached nodes and there are no leaks.
- **Scope selectors** to the component root so `.box` and similar only match elements inside that component, not the rest of the page.

## Vue 3 (Composition API)

For a runnable Vite + Vue 3 project demonstrating these patterns, see the upstream example at https://github.com/greensock/gsap-skills/tree/main/examples/vue.

Use **onMounted** to run GSAP after the component is in the DOM. Use **onUnmounted** to clean up.

```javascript
import { onMounted, onUnmounted, ref } from "vue";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger); // once per app, e.g. in main.js

export default {
  setup() {
    const container = ref(null);
    let ctx;

    onMounted(() => {
      if (!container.value) return;
      ctx = gsap.context(() => {
        gsap.to(".box", { x: 100, duration: 0.6 });
        gsap.from(".item", { autoAlpha: 0, y: 20, stagger: 0.1 });
      }, container.value);
    });

    onUnmounted(() => {
      ctx?.revert();
    });

    return { container };
  },
};
```

- ✅ **gsap.context(scope)** — pass the container ref (e.g. `container.value`) as the second argument so selectors like `.item` are scoped to that root. All animations and ScrollTriggers created inside the callback are tracked and reverted when **ctx.revert()** is called.
- ✅ **onUnmounted** — always call **ctx.revert()** so tweens and ScrollTriggers are killed and inline styles reverted.

## Vue 3 (script setup)

Same idea with `` and refs:

```javascript

import { onMounted, onUnmounted, ref } from "vue";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

const container = ref(null);
let ctx;

onMounted(() => {
  if (!container.value) return;
  ctx = gsap.context(() => {
    gsap.to(".box", { x: 100 });
    gsap.from(".item", { autoAlpha: 0, stagger: 0.1 });
  }, container.value);
});

onUnmounted(() => {
  ctx?.revert();
});

  
    Box
    Item
  

```

## Nuxt 4

> For a runnable Nuxt 4 project with plugin registration, lazy loading, and SSR-safe patterns, see https://github.com/greensock/gsap-skills/tree/main/examples/nuxt.

Use a **reusable composable** to register GSAP Plugins and also to lazy load Plugins that are not extensively used in your application:

```typescript
// composables/useGSAP.ts
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

// In order to dynamically load all the GSAP plugins
const pluginMap = {
  CustomEase: () => import("gsap/CustomEase"),
  Draggable: () => import("gsap/Draggable"),
  CSSRulePlugin: () => import("gsap/CSSRulePlugin"),
  EaselPlugin: () => import("gsap/EaselPlugin"),
  EasePack: () => import("gsap/EasePack"),
  Flip: () => import("gsap/Flip"),
  MotionPathPlugin: () => import("gsap/MotionPathPlugin"),
  Observer: () => import("gsap/Observer"),
  PixiPlugin: () => import("gsap/PixiPlugin"),
  ScrollToPlugin: () => import("gsap/ScrollToPlugin"),
  ScrollTrigger: () => import("gsap/ScrollTrigger"),
  TextPlugin: () => import("gsap/TextPlugin"),
  DrawSVGPlugin: () => import("gsap/DrawSVGPlugin"),
  Physics2DPlugin: () => import("gsap/Physics2DPlugin"),
  PhysicsPropsPlugin: () => import("gsap/PhysicsPropsPlugin"),
  ScrambleTextPlugin: () => import("gsap/ScrambleTextPlugin"),
  CustomBounce: () => import("gsap/CustomBounce"),
  CustomWiggle: () => import("gsap/CustomWiggle"),
  GSDevTools: () => import("gsap/GSDevTools"),
  InertiaPlugin: () => import("gsap/InertiaPlugin"),
  MorphSVGPlugin: () => import("gsap/MorphSVGPlugin"),
  MotionPathHelper: () => import("gsap/MotionPathHelper"),
  ScrollSmoother: () => import("gsap/ScrollSmoother"),
  SplitText: () => import("gsap/SplitText"),
} as const;

type PluginMap = typeof pluginMap;
type LoadablePlugin = keyof PluginMap;

// Resolves the module type for a given key, then picks the named export matching the key
// this allows to have the type definitions for autocomplete in your code editor
type PluginModule = Awaited>;
type PluginExport = PluginModule[K & keyof PluginModule];

export default function () {
  // Register all the GSAP Plugins you want at this point
  gsap.registerPlugin(ScrollTrigger);

  /*
    If you want to lazy load some of the plugins that are
    not widely used in your app (for example in just a couple
    of components or a single route), you can use this method
  */
  async function lazyLoadPlugin(plugin: K): Promise> {
    const loader = pluginMap[plugin];
    const m = await loader();
    const p = (m as any)[plugin];
    gsap.registerPlugin(p);
    return p;
  }

  return {
    gsap,
    ScrollTrigger,
    lazyLoadPlugin,
  };
}
```

Access in components via `useGSAP()`:

```javascript
const { gsap, ScrollTrigger, lazyLoadPlugin } = useGSAP();
```

- ✅ **`useGSAP()`** provides typed access to the gsap instance and lazy load method.
- ✅ **Lazy-load any plugin** (SplitText, MorphSVG, etc.) that is not widely used in your app to reduce initial bundle size.
- ✅ Use **gsap.context(scope)** and **onUnmounted → ctx.revert()** in components, same as Vue 3.

## Svelte

Use **onMount** to run GSAP after the DOM is ready. Use the **returned cleanup function** from onMount (or track the context and clean up in a reactive block / component destroy) to revert. Svelte 5 uses a different lifecycle; the same principle applies: create in “mounted” and revert in “destroyed.”

```javascript

  import { onMount } from "svelte";
  import { gsap } from "gsap";
  import { ScrollTrigger } from "gsap/ScrollTrigger";

  let container;

  onMount(() => {
    if (!container) return;
    const ctx = gsap.context(() => {
      gsap.to(".box", { x: 100 });
      gsap.from(".item", { autoAlpha: 0, stagger: 0.1 });
    }, container);
    return () => ctx.revert();
  });

  Box
  Item

```

- ✅ **bind:this={container}** — get a reference to the root element so you can pass it to **gsap.context(scope)**.
- ✅ **return () => ctx.revert()** — Svelte’s onMount can return a cleanup function; call **ctx.revert()** there so cleanup runs when the component is destroyed.

## Scoping Selectors

Do not use global selectors that can match elements outside the current component. Always pass the **scope** (container element or ref) as the second argument to **gsap.context(callback, scope)** so that any selector run inside the callback is limited to that subtree.

- ✅ **gsap.context(() => { gsap.to(".box", ...) }, containerRef)** — `.box` is only searched inside `containerRef`.
- ❌ Running **gsap.to(".box", ...)** without a context scope in a component can affect other instances or the rest of the page.

## ScrollTrigger Cleanup

ScrollTrigger instances are created when you use the `scrollTrigger` config on a tween/timeline or **ScrollTrigger.create()**. They are **included** in **gsap.context()** and reverted when you call **ctx.revert()**. So:

- Create ScrollTriggers inside the same **gsap.context()** callback you use for tweens.
- Call **ScrollTrigger.refresh()** after layout changes (e.g. after data loads) that affect trigger positions; in Vue/Svelte that often means after the DOM updates (e.g. nextTick in Vue, tick in Svelte, or after async content load).

## When to Create vs Kill

| Lifecycle             | Action                                                                                                            |
| --------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Mounted**           | Create tweens and ScrollTriggers inside **gsap.context(scope)**.                                                  |
| **Unmount / Destroy** | Call **ctx.revert()** so all animations and ScrollTriggers in that context are killed and inline styles reverted. |

Do not create GSAP animations in the component’s setup or in a synchronous top-level script that runs before the root element exists. Wait for **onMounted** / **onMount** (or equivalent) so the container ref is in the DOM.

## Do Not

- ❌ Create tweens or ScrollTriggers before the component is mounted (e.g. in setup without onMounted); the DOM nodes may not exist yet.
- ❌ Use selector strings without a **scope** (pass the container to gsap.context() as the second argument) so selectors don’t match elements outside the component.
- ❌ Skip cleanup; always call **ctx.revert()** in onUnmounted / onMount’s return so animations and ScrollTriggers are killed when the component is destroyed.
- ❌ Register plugins inside a component body that runs every render (it doesn't hurt anything, it's just wasteful); register once at app level.

### Learn More

- **gsap-react** skill for React-specific patterns (useGSAP, contextSafe).

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [nexu-io](https://github.com/nexu-io)
- **Source:** [nexu-io/open-design](https://github.com/nexu-io/open-design)
- **License:** Apache-2.0
- **Homepage:** https://open-design.ai

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-nexu-io-open-design-gsap-frameworks
- Seller: https://agentstack.voostack.com/s/nexu-io
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
