gsap
GSAP is a browser-focused animation platform for tweening numeric values over time with precise sequencing and playback control. The core animates CSS transforms, SVG attributes, colors, strings, canvas state, WebGL values, and plain JavaScript objects; timelines coordinate many tweens. Optional plugins add scroll triggers, dragging, motion paths, FLIP transitions, text splitting, SVG morphing, and other specialized effects. It has no runtime dependencies and includes TypeScript declarations, ESM files, and UMD builds.
GSAP is the best-equipped choice when animation itself is a major product surface and timelines or ScrollTrigger justify the weight and imperative model. For ordinary UI transitions, use the platform or a smaller library, and check the custom license before building visual authoring software.
Use it if
- You need a carefully sequenced animation timeline with labels, overlap, repeats, reversing, seeking, and runtime speed control
- You need consistent animation across DOM, SVG, canvas, WebGL, and plain object values under one API
- You are building scroll-linked interactions that need ScrollTrigger's scrub, pin, snap, callbacks, and refresh model
- You need advanced plugins such as Flip, MorphSVG, MotionPath, SplitText, or Draggable and accept GSAP's license
- You only need a hover, opacity transition, or small entrance effect: CSS transitions, CSS keyframes, or the Web Animations API avoid a 27.4 KB gzipped core dependency
- You require an OSI-approved open-source license: npm identifies GSAP's custom standard license, which permits normal commercial use but prohibits certain no-code visual animation builders that compete with Webflow
- Your team will not budget for animation cleanup and accessibility: GSAP does not automatically honor reduced-motion preferences, and unreverted tweens or ScrollTriggers can leave inline styles, listeners, and layout state behind
- You expect scroll animation to be layout-free: pinning adds spacing, trigger positions are measured, DOM changes may require `ScrollTrigger.refresh()`, and aggressive scrub or pin designs can still harm usability
- You use React or server-rendered components and want a declarative animation model: DOM work belongs on the client, effects need scoped cleanup, and the recommended `useGSAP` hook is a separate package
Setup reality
`npm install gsap` provides the core, all plugin source files, TypeScript declarations, ESM entry points, CommonJS-compatible UMD files under `dist`, and no runtime dependencies. A normal bundler import is `import gsap from 'gsap'`. Plugins use separate paths such as `gsap/ScrollTrigger` and must be passed to `gsap.registerPlugin()` before use; importing `gsap/all` is convenient but gives a bundler more code to consider. The npm package marks itself side-effect-free, so explicit registration also prevents a plugin from disappearing during tree shaking. GSAP can be imported in server-rendered projects, but animations need real elements and should be created after mount in client code. Use `gsap.context()` to scope selector strings and call `revert()` during teardown; React users can install `@gsap/react`, register `useGSAP`, and keep the component client-side. React Strict Mode makes missing cleanup especially visible by running development effects more than once. ScrollTrigger is not part of the active core until registered. It measures the page, can insert pin spacing, and refreshes on resize, but fonts, images, route transitions, or late DOM changes may still require a manual refresh. Responsive and reduced-motion behavior is also opt-in through `gsap.matchMedia()` or your own preference logic. From-tweens record starting values and can cause flashes if CSS does not establish a safe initial state. GSAP writes inline styles and transforms, so use `context.revert()`, tween `revert()`, or careful `kill()` calls instead of leaving mutated DOM across unmounts. The current standard license allows commercial use at no charge and includes formerly paid plugins, but it is not a conventional open-source grant: products that let users visually build animations in competition with Webflow fall under prohibited uses, and the posted terms say future releases may come with revised terms. Teams near that boundary should review the live license rather than relying on the word free.
Patterns
Tween CSS propertiesanimate-element
import gsap from 'gsap';
gsap.to('.card', {
x: 120,
rotation: 8,
opacity: 1,
duration: 0.6,
ease: 'power2.out',
});Transform aliases such as `x` and `rotation` avoid string-building. Selector text is global unless it runs inside a scoped context.
Set explicit start and end statesanimate-from-to
gsap.fromTo(
'.notice',
{ autoAlpha: 0, y: 16 },
{ autoAlpha: 1, y: 0, duration: 0.4, ease: 'power1.out' },
);`autoAlpha` changes opacity and visibility. Establish a safe initial CSS state when a pre-JavaScript flash would be noticeable.
Sequence and overlap tweensbuild-timeline
const tl = gsap.timeline({ defaults: { duration: 0.4 } });
tl.from('.title', { y: 20, autoAlpha: 0 })
.from('.copy', { y: 12, autoAlpha: 0 }, '-=0.2')
.from('.action', { scale: 0.9, autoAlpha: 0 }, '<');The third argument is the position parameter: `-=0.2` overlaps the prior tween and `<` starts at the prior tween's start.
Stagger a group of elementsstagger-list
gsap.from('.result-row', {
y: 10,
opacity: 0,
duration: 0.35,
stagger: { each: 0.06, from: 'start' },
});Large lists create one tween per target. Limit the animated set or batch work when hundreds of nodes may enter together.
Pause, seek, reverse, and change speedcontrol-playback
const intro = gsap.timeline({ paused: true })
.to('.panel', { xPercent: 0, duration: 0.5 })
.addLabel('open');
intro.play();
intro.pause();
intro.seek('open');
intro.timeScale(1.5).reverse();Keep the timeline instance rather than searching the global timeline when UI controls need deterministic ownership.
Scope selectors and revert on teardowncleanup-component
const root = document.querySelector('.widget');
const ctx = gsap.context(() => {
gsap.from('.item', { y: 12, opacity: 0, stagger: 0.05 });
}, root);
function destroy() {
ctx.revert();
}`revert()` kills captured animations and ScrollTriggers and restores their pre-animation state, including inline style changes.
Adapt animations to motion preferencesrespect-reduced-motion
const mm = gsap.matchMedia();
mm.add({
normal: '(prefers-reduced-motion: no-preference)',
reduced: '(prefers-reduced-motion: reduce)',
}, (context) => {
const { reduced } = context.conditions;
gsap.to('.hero', { x: 100, duration: reduced ? 0 : 0.8 });
});
// later
mm.revert();GSAP does not apply reduced-motion policy automatically. MatchMedia reverts captured work when conditions change.
Scrub an animation with scroll positionscroll-trigger-animation
import ScrollTrigger from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
gsap.to('.progress', {
scaleX: 1,
ease: 'none',
scrollTrigger: {
trigger: '.article',
start: 'top top',
end: 'bottom bottom',
scrub: true,
},
});Register the plugin once. Kill or revert the ScrollTrigger during route or component teardown so measurements and listeners do not outlive the DOM.
Pin a section during a timelinepin-scroll-section
const story = gsap.timeline({
scrollTrigger: {
trigger: '.story',
start: 'top top',
end: '+=800',
pin: true,
scrub: 1,
},
});
story.to('.scene-a', { autoAlpha: 0 })
.from('.scene-b', { autoAlpha: 0 });Pinning adds spacing by default and changes page flow. Test keyboard navigation, small screens, zoom, and content loaded after measurement.
Reuse optimized tweens for pointer movementanimate-pointer-input
const xTo = gsap.quickTo('.cursor', 'x', { duration: 0.2, ease: 'power3' });
const yTo = gsap.quickTo('.cursor', 'y', { duration: 0.2, ease: 'power3' });
window.addEventListener('pointermove', (event) => {
xTo(event.clientX);
yTo(event.clientY);
});`quickTo` is designed for repeatedly updated numeric properties. Remove the pointer listener when the feature is destroyed.
Tween SVG attributesanimate-svg-attributes
gsap.to('#meter-circle', {
attr: { 'stroke-dashoffset': 0 },
duration: 1,
ease: 'power2.inOut',
});Use the `attr` object for SVG attributes. CSS properties such as transforms still belong at the top level of the tween vars.
Tween a plain object for canvas renderinganimate-object-value
const state = { progress: 0 };
gsap.to(state, {
progress: 1,
duration: 1.2,
onUpdate() {
drawFrame(state.progress);
},
});GSAP drives the value but does not schedule canvas clearing or drawing for you; keep `onUpdate` work within the frame budget.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| motion | npm | You want a modern animation library with compact DOM APIs, layout animation, gestures, and framework integrations |
| animejs | npm | You want a smaller timeline-oriented library for DOM, SVG, and object animation with a conventional open-source license |
| @motionone/dom | npm | You want a focused Web Animations API layer for DOM effects and do not need GSAP's plugin platform |