Composables

useGsap()

Access the GSAP instance or create a scoped animation context with automatic cleanup.

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.

components/AnimatedBox.vue
<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:

components/TimelineBox.vue
<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>

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

  1. Auto-cleanup — All tweens, timelines, and plugin instances (ScrollTrigger, Draggable, etc.) are reverted automatically without a single onUnmounted.
  2. SSR-safe — Returns { contextSafe: fn => fn } on the server, so no DOM access during SSR builds.
  3. Page-transition aware — Cleanup is deferred until after the leave transition finishes, so exit animations play smoothly through the fade-out.
  4. Scoped selectors — The scope option restricts CSS class/id selectors to a subtree, preventing selector leaks to same-class elements in other components.
  5. Reactive re-runs — Pass dependencies to 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 })
components/ContextBox.vue
<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

OptionTypeDefaultDescription
scopeRef<HTMLElement | null>undefinedScopes class/id selectors inside the element
dependenciesWatchSource[][]Re-run setup when any source changes
revertOnUpdatebooleantrueRevert the previous context before re-running
cleanupOn'unmount' | 'route-leave''unmount'Deprecated, no-op. Both values produce identical behavior. Kept for backward compatibility.
Page transition behaviorWhen navigating, 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>
Call the plugin composable outside useGsap — you only need the instance for static methods like 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 = []
})
Plugin registration is handled once by the module at app startup. In components, only kill the instances you created (tweens, timelines, Draggables, ScrollTriggers). Do not call gsap.registerPlugin() manually.
Copyright © 2026