useGsap()
useGsap() is auto-imported in every component and composable. It supports two call signatures.
Which form should I use?
- Zero-argument — Quick prototyping or when you need full manual control over cleanup. You manage timelines, contexts, and plugin instances yourself.
- Context form (recommended) — For most components. Automatic cleanup, SSR-safe, page-transition aware, and scoped to your element. No boilerplate.
Zero-argument form
Returns the raw gsap instance. Use this when you want full control and will manage cleanup manually.
<script setup lang="ts">
const gsap = useGsap()
const boxRef = ref<HTMLElement | null>(null)
onMounted(() => {
gsap.from(boxRef.value, { opacity: 0, scale: 0.8, duration: 0.5 })
})
</script>
<template>
<div ref="boxRef" class="box">Hello!</div>
</template>
You can also use the auto-imported gsap global directly — both are equivalent.
Timeline example:
<script setup lang="ts">
const gsap = useGsap()
const boxRef = ref<HTMLElement | null>(null)
onMounted(() => {
const tl = gsap.timeline({ repeat: -1, yoyo: true })
tl.to(boxRef.value, { x: 200, duration: 1, ease: 'power2.inOut' })
.to(boxRef.value, { rotation: 360, duration: 0.8 })
})
</script>
<template>
<div ref="boxRef" class="box" />
</template>
Context form (recommended)
Pass a setup function to wrap your animations in a gsap.context(). The context is automatically reverted when the component unmounts or the page transitions away. No cleanup boilerplate needed.
Advantages
- Auto-cleanup — All tweens, timelines, and plugin instances (ScrollTrigger, Draggable, etc.) are reverted automatically without a single
onUnmounted. - SSR-safe — Returns
{ contextSafe: fn => fn }on the server, so no DOM access during SSR builds. - Page-transition aware — Cleanup is deferred until after the leave transition finishes, so exit animations play smoothly through the fade-out.
- Scoped selectors — The
scopeoption restricts CSS class/id selectors to a subtree, preventing selector leaks to same-class elements in other components. - Reactive re-runs — Pass
dependenciesto re-run the setup function when reactive values change, with automatic context revert + re-run.
Before and after
Without context form (manual cleanup):
const gsap = useGsap()
let ctx: gsap.Context
onMounted(() => {
ctx = gsap.context(() => {
gsap.from('.box', { opacity: 0, y: 30 })
ScrollTrigger.create({ trigger: '.box', start: 'top 80%' })
})
})
onUnmounted(() => ctx.revert()) // must remember this
With context form (recommended):
useGsap(() => {
gsap.from('.box', { opacity: 0, y: 30 })
ScrollTrigger.create({ trigger: '.box', start: 'top 80%' })
// auto-reverted — ScrollTrigger too
}, { scope: containerRef })
<script setup lang="ts">
const containerRef = ref<HTMLElement | null>(null)
useGsap(() => {
gsap.from('.box', { opacity: 0, y: 30, duration: 0.6, ease: 'power2.out' })
}, { scope: containerRef })
</script>
<template>
<div ref="containerRef">
<div class="box">I animate in safely</div>
</div>
</template>
Options
| Option | Type | Default | Description |
|---|---|---|---|
scope | Ref<HTMLElement | null> | undefined | Scopes class/id selectors inside the element |
dependencies | WatchSource[] | [] | Re-run setup when any source changes |
revertOnUpdate | boolean | true | Revert the previous context before re-running |
cleanupOn | 'unmount' | 'route-leave' | 'unmount' | Deprecated, no-op. Both values produce identical behavior. Kept for backward compatibility. |
useGsap(setup) defers context cleanup until after the page leave transition
(page:transition:finish). Animations continue playing through the fade-out on every page,
with no extra configuration required.The cleanupOn option is kept for backward compatibility but is now a no-op — both
'unmount' and 'route-leave' produce identical behavior.If navigation is cancelled (a route guard rejects), the context stays alive and no revert happens.contextSafe
The setup callback receives a self reference to the GSAP context. contextSafe is returned so you can wrap event handlers that create animations after mount:
const { contextSafe } = useGsap((self) => {
// initial animations
}, { scope: containerRef })
const handleClick = contextSafe(() => {
gsap.to('.box', { rotation: '+=90' })
})
With reactive dependencies
const color = ref('red')
useGsap(() => {
gsap.to('.box', { backgroundColor: color.value, duration: 0.3 })
}, { dependencies: [color] })
Every time color changes the context reverts and re-runs (controlled by revertOnUpdate).
Using with plugin composables
You can call plugin composables inside the setup function to access static methods (getAll(), refresh(), create()). The key is: call the composable outside useGsap to get the instance, but run animations (and their cleanup) inside the setup function.
With ScrollTrigger
<script setup lang="ts">
const ScrollTrigger = useScrollTrigger()
const containerRef = ref<HTMLElement | null>(null)
useGsap(() => {
gsap.from('.card', {
opacity: 0,
y: 40,
stagger: 0.1,
scrollTrigger: {
trigger: '.card',
start: 'top 85%',
end: 'top 50%',
scrub: true,
},
})
// ScrollTrigger.refresh() available via the composable outside
}, { scope: containerRef })
</script>
<template>
<div ref="containerRef">
<div class="card">Card 1</div>
<div class="card">Card 2</div>
</div>
</template>
refresh() or getAll(). The animations and ScrollTriggers created inside the setup function are what gets auto-reverted when the component unmounts.With Draggable and contextSafe
Event handlers that add animations after mount should be wrapped with contextSafe:
const Draggable = useDraggable()
const { contextSafe } = useGsap(() => {
Draggable.create(boxRef.value, { type: 'x,y' })
})
const handleDragEnd = contextSafe(() => {
gsap.to(boxRef.value, { scale: 1.1, duration: 0.2 })
})
All animations and Draggable instances created inside contextSafe are part of the same context and get auto-reverted.
Zero-arg form: cleanup guide
Use these patterns only when using the zero-argument form of useGsap() and you need manual control. For most components, prefer the context form above.
gsap.context() — covers all child tweens, timelines, and ScrollTriggers:
<script setup lang="ts">
const gsap = useGsap()
let ctx: gsap.Context
onMounted(() => {
ctx = gsap.context(() => {
gsap.to('.box', { x: 100 })
})
})
onUnmounted(() => ctx.revert())
</script>
Single timeline:
const gsap = useGsap()
const tl = gsap.timeline()
onUnmounted(() => tl.kill())
Plugin instance (e.g. Draggable):
const Draggable = useDraggable()
let draggables: ReturnType<typeof Draggable.create> = []
onMounted(() => {
draggables = Draggable.create(boxRef.value)
})
onUnmounted(() => {
draggables.forEach(d => d.kill())
draggables = []
})
gsap.registerPlugin() manually.