@lpdsgn/gsap-spa-manager
v0.1.0
Published
Framework-agnostic GSAP animation manager for SPAs with automatic cleanup, persistence, and ScrollTrigger support
Maintainers
Readme
GSAP SPA Manager
A framework-agnostic GSAP animation manager for Single Page Applications with automatic cleanup, persistence, and ScrollTrigger support.
Features
- Framework Agnostic - Works with Swup, Barba.js, Astro View Transitions, or standalone
- Automatic Cleanup - Animations are automatically killed on page transitions
- Persistence Support - Mark animations as persistent to survive page transitions
- ScrollTrigger Management - Centralized ScrollTrigger registration and cleanup
- Context Management - Uses
gsap.context()for proper cleanup of side-effects - Multiple Formats - ESM, CJS, and UMD builds available
- Debug Mode - Comprehensive logging for development
Installation
npm install gsap-spa-manager gsap
# or
pnpm add gsap-spa-manager gsap
# or
yarn add gsap-spa-manager gsapQuick Start
Standalone Usage (No SPA Router)
import { AM } from 'gsap-spa-manager';
import { gsap } from 'gsap';
// Initialize (optional, enables debug mode)
AM.init({ debug: true });
// Create a simple animation
AM.animate('hero', gsap.from('.hero', { opacity: 0, y: 50 }));
// Create a timeline
const tl = AM.timeline('intro');
tl.from('.title', { opacity: 0 })
.from('.subtitle', { opacity: 0 }, '-=0.3');
// Use setup for complex animations with cleanup
AM.setup('mySection', (ctx) => {
gsap.to('.box', { rotation: 360, repeat: -1 });
// Register side-effects with automatic cleanup
const onClick = () => console.log('clicked');
document.addEventListener('click', onClick);
ctx?.add(() => () => document.removeEventListener('click', onClick));
});
// Cleanup when done
AM.cleanup('hero');With Swup
import Swup from 'swup';
import { AM, swupAdapter } from 'gsap-spa-manager';
const swup = new Swup();
AM.init({
debug: true,
adapter: swupAdapter(swup)
});
// Animations will now automatically cleanup on page transitions
AM.setup('pageAnimations', () => {
gsap.from('.content', { opacity: 0 });
});With Barba.js
import barba from '@barba/core';
import { AM, barbaAdapter } from 'gsap-spa-manager';
barba.init();
AM.init({
debug: true,
adapter: barbaAdapter(barba)
});With Astro View Transitions
import { AM, astroAdapter } from 'gsap-spa-manager';
// Astro adapter uses native DOM events, no router instance needed
AM.init({
debug: true,
adapter: astroAdapter()
});
// Optional: add delay before ScrollTrigger refresh
AM.init({
adapter: astroAdapter({ refreshDelay: 100 })
});UMD (Browser Global)
<script src="https://cdn.jsdelivr.net/npm/gsap"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap/ScrollTrigger"></script>
<script src="https://unpkg.com/gsap-spa-manager/dist/index.umd.js"></script>
<script>
const { AM } = AnimationManager;
AM.init({ debug: true });
AM.animate('box', gsap.to('.box', { x: 100 }));
</script>API Reference
AM.init(options)
Initialize the Animation Manager.
AM.init({
debug: boolean, // Enable debug logging (default: false)
adapter: SPAAdapter // Optional SPA adapter for automatic cleanup
});AM.destroy()
Disconnect the adapter and force cleanup all animations.
AM.destroy();AM.animate(key, animation, options?)
Register a GSAP animation (Tween or Timeline).
// Single animation
AM.animate('myAnimation', gsap.to('.el', { x: 100 }));
// Multiple animations under one key
AM.animate('myAnimations', [
gsap.to('.el1', { x: 100 }),
gsap.to('.el2', { y: 50 })
]);
// With persistence (survives page transitions)
AM.animate('persistent', gsap.to('.el', { x: 100 }), { persist: true });AM.timeline(key, vars?, options?)
Create and register a GSAP Timeline.
const tl = AM.timeline('myTimeline', { paused: true });
tl.to('.el', { x: 100 })
.to('.el', { y: 50 });
// Persistent timeline
const persistentTl = AM.timeline('navbar', { paused: true }, { persist: true });AM.setup(key, setupFn, options?)
Execute a setup function within a gsap.context() for proper cleanup.
AM.setup('mySetup', (ctx) => {
// All GSAP animations here are tracked
gsap.to('.el', { x: 100 });
gsap.from('.other', { opacity: 0 });
// Register non-GSAP side-effects for automatic cleanup
ctx?.add(() => {
const handler = () => {};
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
});
}, {
persist: true, // Survive cleanup
scope: document.body // Scope for GSAP selectors
});AM.scroll(key, vars, options?)
Create and register a ScrollTrigger.
AM.scroll('myTrigger', {
trigger: '.section',
start: 'top center',
end: 'bottom center',
scrub: true,
animation: gsap.to('.el', { x: 100 })
});
// Persistent ScrollTrigger
AM.scroll('navTrigger', {
trigger: '.header',
start: 'top top',
pin: true
}, { persist: true });AM.register(key, animation, options?)
Register an externally created animation. Useful when you need more control over animation creation.
const myTween = gsap.to('.el', { x: 100, paused: true });
AM.register('myTween', myTween);
// Register multiple animations
AM.register('batch', [tween1, tween2, timeline1]);AM.registerScrollTriggers(key, triggers, options?)
Register externally created ScrollTriggers.
const st1 = ScrollTrigger.create({ trigger: '.a', ... });
const st2 = ScrollTrigger.create({ trigger: '.b', ... });
AM.registerScrollTriggers('myTriggers', [st1, st2]);AM.cleanup(key, force?)
Cleanup a specific animation by key.
AM.cleanup('myAnimation');
AM.cleanup('persistentAnim', true); // Force cleanup even if persistentAM.cleanupAll()
Cleanup all non-persistent animations.
AM.cleanupAll();AM.forceCleanupAll()
Cleanup everything, including persistent animations.
AM.forceCleanupAll();AM.refresh(keys?)
Refresh ScrollTriggers.
AM.refresh(); // Refresh all
AM.refresh('myTrigger'); // Refresh specific
AM.refresh(['a', 'b']); // Refresh multipleAM.removePersistence(key)
Remove persistence from an animation, making it eligible for cleanup.
AM.removePersistence('myAnimation');AM.getStatus()
Get current manager status.
const status = AM.getStatus();
// {
// animations: 5,
// scrollTriggers: 3,
// persistent: 2,
// keys: ['hero', 'intro', ...],
// persistentKeys: ['navbar', 'footer']
// }AM.isActive(key)
Check if an animation/setup is active.
if (AM.isActive('hero')) {
// Animation exists
}AM.isPersistent(key)
Check if an animation is marked as persistent.
if (AM.isPersistent('navbar')) {
// Won't be cleaned up on page transitions
}AM.debug()
Show detailed debug information in the console (only works if debug mode is enabled).
AM.debug();Creating Custom Adapters
You can create adapters for any SPA router:
import type { SPAAdapter } from 'gsap-spa-manager';
const myAdapter: SPAAdapter = {
name: 'MyRouter',
onBeforeSwap(callback) {
// Called before page content is replaced
myRouter.on('before:navigate', callback);
},
onAfterSwap(callback) {
// Called after page content is replaced
myRouter.on('after:navigate', callback);
},
destroy() {
// Optional: cleanup when AM.destroy() is called
myRouter.off('before:navigate');
myRouter.off('after:navigate');
}
};
AM.init({ adapter: myAdapter });Best Practices
Use
setup()for complex animations - It provides automatic cleanup viagsap.context()and is ideal when you have multiple related animations and side-effects.Register all ScrollTriggers - Use
AM.scroll()instead ofScrollTrigger.create()directly to ensure proper cleanup on page transitions.Use persistence sparingly - Only for animations that truly need to survive page transitions (e.g., navbar, persistent UI elements).
Clean up manually when needed - Call
AM.cleanup(key)when removing elements dynamically, not just on page transitions.Use debug mode during development - It helps track animation lifecycle and identify cleanup issues.
Use unique keys - Keys are used to track and cleanup animations. Reusing keys will skip registration (useful for preventing duplicates).
TypeScript Support
Full TypeScript support with exported types:
import type {
SPAAdapter,
AnimationManagerOptions,
AnimationOptions,
SetupOptions,
AstroAdapterOptions
} from 'gsap-spa-manager';Browser Support
The UMD build exposes AnimationManager globally. The singleton instance AM is also available on window.AM for debugging in browser DevTools.
License
MIT
