diff --git a/examples/README.md b/examples/README.md
index 03f4c28..4d565dc 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,6 +1,6 @@
# GSAP skills — reference examples
-Minimal demos that follow the skills exactly: transforms, autoAlpha, timelines, ScrollTrigger, and (in React) useGSAP with scope and cleanup.
+Minimal demos that follow the skills exactly: transforms, autoAlpha, timelines, ScrollTrigger, and framework-specific patterns (useGSAP in React, onMount/onUnmount with gsap.context in Vue/Nuxt).
## Vanilla (HTML + JS)
@@ -15,3 +15,15 @@ Minimal demos that follow the skills exactly: transforms, autoAlpha, timelines,
- Patterns: `useGSAP()` with `scope: containerRef`, refs for targets, no selectors without scope; cleanup is automatic on unmount.
These examples are intended as reference implementations for AI agents and for quick manual verification of the skill patterns.
+
+## Vue
+
+- **examples/vue/** — Vite + Vue 3 + `
+
+
+
+ GSAP SplitText
+
+
+
+
\ No newline at end of file
diff --git a/examples/nuxt/nuxt.config.ts b/examples/nuxt/nuxt.config.ts
new file mode 100644
index 0000000..f773605
--- /dev/null
+++ b/examples/nuxt/nuxt.config.ts
@@ -0,0 +1,4 @@
+export default defineNuxtConfig({
+ ssr: true,
+ compatibilityDate: '2025-07-15',
+});
\ No newline at end of file
diff --git a/examples/nuxt/package.json b/examples/nuxt/package.json
new file mode 100644
index 0000000..bfbb05c
--- /dev/null
+++ b/examples/nuxt/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "gsap-nuxt-example",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "build": "nuxt build",
+ "dev": "nuxt dev",
+ "generate": "nuxt generate",
+ "preview": "nuxt preview",
+ "postinstall": "nuxt prepare"
+ },
+ "dependencies": {
+ "gsap": "^3.14.2",
+ "nuxt": "^4.4.2",
+ "vue": "^3.5.30",
+ "vue-router": "^5.0.4"
+ }
+}
\ No newline at end of file
diff --git a/examples/vue/app.vue b/examples/vue/app.vue
new file mode 100644
index 0000000..209550b
--- /dev/null
+++ b/examples/vue/app.vue
@@ -0,0 +1,47 @@
+/**
+ * Minimal GSAP Vue 3 example — follows gsap-frameworks skill: gsap.context, scope, cleanup.
+ * Scoped selectors via context; automatic revert on unmount.
+ */
+
+
+
+
+
GSAP Vue 3 — gsap.context + scope + cleanup
+
+
+
+
Item 1
+
Item 2
+
Item 3
+
+
+
+
\ No newline at end of file
diff --git a/examples/vue/index.html b/examples/vue/index.html
new file mode 100644
index 0000000..576c19d
--- /dev/null
+++ b/examples/vue/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ GSAP Vue 3 — gsap.context + scope + cleanup
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/vue/main.js b/examples/vue/main.js
new file mode 100644
index 0000000..e76a2f6
--- /dev/null
+++ b/examples/vue/main.js
@@ -0,0 +1,9 @@
+import { createApp } from "vue";
+import { gsap } from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+import App from "./App.vue";
+
+// Register all the GSAP Plugins you want only once at this top level file
+gsap.registerPlugin(ScrollTrigger);
+
+createApp(App).mount("#app");
\ No newline at end of file
diff --git a/examples/vue/package.json b/examples/vue/package.json
new file mode 100644
index 0000000..c9ffbd4
--- /dev/null
+++ b/examples/vue/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "gsap-vue-example",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "gsap": "^3.14.2",
+ "vue": "^3.5.32"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^6.0.5",
+ "vite": "^8.0.4"
+ }
+}
\ No newline at end of file
diff --git a/examples/vue/vite.config.js b/examples/vue/vite.config.js
new file mode 100644
index 0000000..37d3d6f
--- /dev/null
+++ b/examples/vue/vite.config.js
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [vue()],
+});
diff --git a/skills/gsap-frameworks/SKILL.md b/skills/gsap-frameworks/SKILL.md
index 1ffb90b..3f8af87 100644
--- a/skills/gsap-frameworks/SKILL.md
+++ b/skills/gsap-frameworks/SKILL.md
@@ -20,6 +20,8 @@ Apply when writing or reviewing GSAP code in Vue (or Nuxt), Svelte (or SvelteKit
## Vue 3 (Composition API)
+See `examples/vue/` for a runnable Vite + Vue 3 project demonstrating these patterns.
+
Use **onMounted** to run GSAP after the component is in the DOM. Use **onUnmounted** to clean up.
```javascript
@@ -46,7 +48,7 @@ export default {
});
return { container };
- }
+ },
};
```
@@ -87,6 +89,120 @@ onUnmounted(() => {
```
+## Nuxt 3
+
+> See `examples/nuxt/` for a runnable Nuxt 4 project with plugin registration, lazy loading, and SSR-safe patterns.
+
+Use a **client-only composable** (`.client.ts` suffix) to register GSAP Plugins, it never runs on the server:
+
+```typescript
+// composables/useGSAP.client.ts
+import { gsap } from "gsap";
+import { ScrollTrigger } from "gsap/ScrollTrigger";
+
+const PLUGINS = [
+ "CSSRulePlugin",
+ "CustomBounce",
+ "CustomEase",
+ "CustomWiggle",
+ "Draggable",
+ "DrawSVGPlugin",
+ "EaselPlugin",
+ "EasePack",
+ "Flip",
+ "GSDevTools",
+ "InertiaPlugin",
+ "MorphSVGPlugin",
+ "MotionPathHelper",
+ "MotionPathPlugin",
+ "Observer",
+ "Physics2DPlugin",
+ "PhysicsPropsPlugin",
+ "PixiPlugin",
+ "ScrambleTextPlugin",
+ "ScrollSmoother",
+ "ScrollToPlugin",
+ "ScrollTrigger",
+ "SplitText",
+ "TextPlugin",
+] as const;
+
+type Plugins = (typeof PLUGINS)[number];
+
+// 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 Plugins = 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
+import useGSAP from "./composables/useGSAP.client";
+
+const { gsap, ScrollTrigger, lazyLoadPlugin } = useGSAP();
+```
+
+- ✅ **`.client.ts` suffix** ensures the composable only runs in the browser (SSR-safe). Never import GSAP in server-rendered code.
+- ✅ **`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.”
@@ -134,9 +250,9 @@ ScrollTrigger instances are created when you use the `scrollTrigger` config on a
## When to Create vs Kill
-| Lifecycle | Action |
-|-----------------|--------|
-| **Mounted** | Create tweens and ScrollTriggers inside **gsap.context(scope)**. |
+| 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.