# Animation Libraries Reference
## Framer Motion
The most popular React animation library with declarative API.
### Basic Animations
```tsx
import { motion, AnimatePresence } from "framer-motion";
// Simple animation
function FadeIn({ children }) {
return (
{children}
);
}
// Gesture animations
function InteractiveCard() {
return (
Hover or tap me
);
}
// Keyframes animation
function PulseButton() {
return (
Click me
);
}
```
### Layout Animations
```tsx
import { motion, LayoutGroup } from "framer-motion";
// Shared layout animation
function TabIndicator({ activeTab, tabs }) {
return (
{tabs.map((tab) => (
))}
);
}
// Auto-layout reordering
function ReorderableList({ items, setItems }) {
return (
{items.map((item) => (
{item.title}
))}
);
}
```
### Orchestration
```tsx
// Staggered children
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.3 },
},
};
function StaggeredList({ items }) {
return (
{items.map((item) => (
{item.content}
))}
);
}
```
### Page Transitions
```tsx
import { AnimatePresence, motion } from "framer-motion";
import { useRouter } from "next/router";
const pageVariants = {
initial: { opacity: 0, x: -20 },
enter: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 20 },
};
function PageTransition({ children }) {
const router = useRouter();
return (
{children}
);
}
```
## GSAP (GreenSock)
Industry-standard animation library for complex, performant animations.
### Basic Timeline
```tsx
import { useRef, useLayoutEffect } from "react";
import gsap from "gsap";
function AnimatedHero() {
const containerRef = useRef(null);
const titleRef = useRef(null);
const subtitleRef = useRef(null);
useLayoutEffect(() => {
const ctx = gsap.context(() => {
const tl = gsap.timeline({ defaults: { ease: "power3.out" } });
tl.from(titleRef.current, {
y: 50,
opacity: 0,
duration: 0.8,
})
.from(
subtitleRef.current,
{
y: 30,
opacity: 0,
duration: 0.6,
},
"-=0.4", // Start 0.4s before previous ends
)
.from(".cta-button", {
scale: 0.8,
opacity: 0,
duration: 0.4,
});
}, containerRef);
return () => ctx.revert(); // Cleanup
}, []);
return (
Welcome
Discover amazing things
);
}
```
### ScrollTrigger
```tsx
import { useLayoutEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
function ParallaxSection() {
const sectionRef = useRef(null);
const imageRef = useRef(null);
useLayoutEffect(() => {
const ctx = gsap.context(() => {
// Parallax image
gsap.to(imageRef.current, {
yPercent: -20,
ease: "none",
scrollTrigger: {
trigger: sectionRef.current,
start: "top bottom",
end: "bottom top",
scrub: true,
},
});
// Fade in content
gsap.from(".content-block", {
opacity: 0,
y: 50,
stagger: 0.2,
scrollTrigger: {
trigger: sectionRef.current,
start: "top 80%",
end: "top 20%",
scrub: 1,
},
});
}, sectionRef);
return () => ctx.revert();
}, []);
return (
);
}
```
### Text Animation
```tsx
import { useLayoutEffect, useRef } from "react";
import gsap from "gsap";
import { SplitText } from "gsap/SplitText";
gsap.registerPlugin(SplitText);
function AnimatedHeadline({ text }) {
const textRef = useRef(null);
useLayoutEffect(() => {
const split = new SplitText(textRef.current, {
type: "chars,words",
charsClass: "char",
});
gsap.from(split.chars, {
opacity: 0,
y: 50,
rotateX: -90,
stagger: 0.02,
duration: 0.8,
ease: "back.out(1.7)",
});
return () => split.revert();
}, [text]);
return {text}
;
}
```
## CSS Spring Physics
```tsx
// spring.ts - Custom spring physics
interface SpringConfig {
stiffness: number; // Higher = snappier
damping: number; // Higher = less bouncy
mass: number;
}
const presets: Record = {
default: { stiffness: 170, damping: 26, mass: 1 },
gentle: { stiffness: 120, damping: 14, mass: 1 },
wobbly: { stiffness: 180, damping: 12, mass: 1 },
stiff: { stiffness: 210, damping: 20, mass: 1 },
slow: { stiffness: 280, damping: 60, mass: 1 },
molasses: { stiffness: 280, damping: 120, mass: 1 },
};
function springToCss(config: SpringConfig): string {
// Convert spring parameters to CSS timing function approximation
const { stiffness, damping } = config;
const duration = Math.sqrt(stiffness) / damping;
const bounce = 1 - damping / (2 * Math.sqrt(stiffness));
// Map to cubic-bezier (approximation)
if (bounce <= 0) {
return `cubic-bezier(0.25, 0.1, 0.25, 1)`;
}
return `cubic-bezier(0.34, 1.56, 0.64, 1)`;
}
```
## Web Animations API
Native browser animation API for simple animations.
```tsx
function useWebAnimation(
ref: RefObject,
keyframes: Keyframe[],
options: KeyframeAnimationOptions,
) {
useEffect(() => {
if (!ref.current) return;
const animation = ref.current.animate(keyframes, options);
return () => animation.cancel();
}, [ref, keyframes, options]);
}
// Usage
function SlideIn({ children }) {
const elementRef = useRef(null);
useWebAnimation(
elementRef,
[
{ transform: "translateX(-100%)", opacity: 0 },
{ transform: "translateX(0)", opacity: 1 },
],
{
duration: 300,
easing: "cubic-bezier(0.16, 1, 0.3, 1)",
fill: "forwards",
},
);
return {children}
;
}
```
## View Transitions API
Native browser API for page transitions.
```tsx
// Check support
const supportsViewTransitions = "startViewTransition" in document;
// Simple page transition
async function navigateTo(url: string) {
if (!document.startViewTransition) {
window.location.href = url;
return;
}
document.startViewTransition(async () => {
await fetch(url);
// Update DOM
});
}
// Named elements for morphing
function ProductCard({ product }) {
return (
);
}
// CSS for view transitions
/*
::view-transition-old(root) {
animation: fade-out 0.25s ease-out;
}
::view-transition-new(root) {
animation: fade-in 0.25s ease-in;
}
::view-transition-group(product-*) {
animation-duration: 0.3s;
}
*/
```
## Performance Tips
### GPU Acceleration
```css
/* Properties that trigger GPU acceleration */
.animated-element {
transform: translateZ(0); /* Force GPU layer */
will-change: transform, opacity; /* Hint to browser */
}
/* Only animate transform and opacity for 60fps */
.smooth {
transition:
transform 0.3s ease,
opacity 0.3s ease;
}
/* Avoid animating these (cause reflow) */
.avoid {
/* Don't animate: width, height, top, left, margin, padding */
}
```
### Reduced Motion
```tsx
function useReducedMotion() {
const [prefersReduced, setPrefersReduced] = useState(false);
useEffect(() => {
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
setPrefersReduced(mq.matches);
const handler = (e: MediaQueryListEvent) => setPrefersReduced(e.matches);
mq.addEventListener("change", handler);
return () => mq.removeEventListener("change", handler);
}, []);
return prefersReduced;
}
// Usage
function AnimatedComponent() {
const prefersReduced = useReducedMotion();
return (
Content
);
}
```