Create stunning animations with Framer Motion for React. Learn gestures, layout animations, scroll effects, exit animations, orchestration, and performance optimization patterns.
# Framer Motion Animations Complete Guide
Build beautiful, performant animations with Framer Motion for React applications including gestures, layout transitions, and scroll effects.
## Basic Animations
### Motion Components
```typescript
// components/AnimatedCard.tsx
"use client";
import { motion } from "framer-motion";
export function AnimatedCard({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{
duration: 0.3,
ease: "easeOut",
}}
whileHover={{
scale: 1.02,
boxShadow: "0 10px 30px rgba(0,0,0,0.1)",
}}
whileTap={{ scale: 0.98 }}
className="bg-white rounded-xl p-6 shadow-md"
>
{children}
</motion.div>
);
}
```
### Animation Variants
```typescript
// lib/animation-variants.ts
import { Variants } from "framer-motion";
export const fadeInUp: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: "easeOut" },
},
};
export const staggerContainer: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};
export const scaleOnHover: Variants = {
rest: { scale: 1 },
hover: {
scale: 1.05,
transition: { duration: 0.2, ease: "easeInOut" },
},
tap: { scale: 0.95 },
};
export const slideInFromLeft: Variants = {
hidden: { x: -100, opacity: 0 },
visible: {
x: 0,
opacity: 1,
transition: { type: "spring", stiffness: 100, damping: 15 },
},
};
```
### Staggered Lists
```typescript
// components/AnimatedList.tsx
"use client";
import { motion } from "framer-motion";
import { staggerContainer, fadeInUp } from "@/lib/animation-variants";
interface AnimatedListProps {
items: Array<{ id: string; title: string; description: string }>;
}
export function AnimatedList({ items }: AnimatedListProps) {
return (
<motion.ul
variants={staggerContainer}
initial="hidden"
animate="visible"
className="space-y-4"
>
{items.map((item) => (
<motion.li
key={item.id}
variants={fadeInUp}
className="bg-white p-4 rounded-lg shadow"
>
<h3 className="font-bold">{item.title}</h3>
<p className="text-gray-600">{item.description}</p>
</motion.li>
))}
</motion.ul>
);
}
```
## Layout Animations
### Shared Layout
```typescript
// components/TabsWithAnimation.tsx
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
const tabs = ["Home", "About", "Contact"];
export function AnimatedTabs() {
const [activeTab, setActiveTab] = useState(0);
return (
<div>
<div className="flex gap-2 border-b">
{tabs.map((tab, index) => (
<button
key={tab}
onClick={() => setActiveTab(index)}
className="relative px-4 py-2"
>
{tab}
{activeTab === index && (
<motion.div
layoutId="activeTab"
className="absolute bottom-0 left-0 right-0 h-0.5 bg-blue-600"
transition={{ type: "spring", stiffness: 500, damping: 30 }}
/>
)}
</button>
))}
</div>
<AnimatePresence mode="wait">
<motion.div
key={activeTab}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.2 }}
className="p-4"
>
Content for {tabs[activeTab]}
</motion.div>
</AnimatePresence>
</div>
);
}
```
### Reorder Animation
```typescript
// components/ReorderableList.tsx
"use client";
import { useState } from "react";
import { Reorder, useDragControls } from "framer-motion";
interface Item {
id: string;
text: string;
}
export function ReorderableList() {
const [items, setItems] = useState<Item[]>([
{ id: "1", text: "First item" },
{ id: "2", text: "Second item" },
{ id: "3", text: "Third item" },
]);
return (
<Reorder.Group
axis="y"
values={items}
onReorder={setItems}
className="space-y-2"
>
{items.map((item) => (
<ReorderItem key={item.id} item={item} />
))}
</Reorder.Group>
);
}
function ReorderItem({ item }: { item: Item }) {
const controls = useDragControls();
return (
<Reorder.Item
value={item}
dragListener={false}
dragControls={controls}
className="flex items-center gap-4 p-4 bg-white rounded-lg shadow cursor-grab active:cursor-grabbing"
whileDrag={{ scale: 1.02, boxShadow: "0 5px 20px rgba(0,0,0,0.15)" }}
>
<button
onPointerDown={(e) => controls.start(e)}
className="cursor-grab"
>
⋮⋮
</button>
<span>{item.text}</span>
</Reorder.Item>
);
}
```
## Scroll Animations
### Scroll-Triggered Animations
```typescript
// components/ScrollReveal.tsx
"use client";
import { motion, useInView } from "framer-motion";
import { useRef } from "react";
export function ScrollReveal({ children }: { children: React.ReactNode }) {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: "-100px" });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 50 }}
transition={{ duration: 0.5, ease: "easeOut" }}
>
{children}
</motion.div>
);
}
```
### Parallax Scroll
```typescript
// components/ParallaxSection.tsx
"use client";
import { motion, useScroll, useTransform } from "framer-motion";
import { useRef } from "react";
export function ParallaxSection() {
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end start"],
});
const y = useTransform(scrollYProgress, [0, 1], ["0%", "50%"]);
const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);
const scale = useTransform(scrollYProgress, [0, 0.5, 1], [0.8, 1, 0.8]);
return (
<section ref={ref} className="relative h-screen overflow-hidden">
<motion.div
style={{ y }}
className="absolute inset-0 bg-gradient-to-b from-blue-500 to-purple-600"
/>
<motion.div
style={{ opacity, scale }}
className="relative z-10 flex items-center justify-center h-full"
>
<h2 className="text-6xl font-bold text-white">Parallax Effect</h2>
</motion.div>
</section>
);
}
```
## Gesture Animations
```typescript
// components/DraggableCard.tsx
"use client";
import { motion, useMotionValue, useTransform } from "framer-motion";
export function SwipeCard({ onSwipe }: { onSwipe: (direction: "left" | "right") => void }) {
const x = useMotionValue(0);
const rotate = useTransform(x, [-200, 200], [-30, 30]);
const opacity = useTransform(x, [-200, -100, 0, 100, 200], [0.5, 1, 1, 1, 0.5]);
return (
<motion.div
drag="x"
dragConstraints={{ left: 0, right: 0 }}
style={{ x, rotate, opacity }}
onDragEnd={(_, info) => {
if (info.offset.x > 100) onSwipe("right");
if (info.offset.x < -100) onSwipe("left");
}}
className="w-72 h-96 bg-white rounded-2xl shadow-xl cursor-grab active:cursor-grabbing"
>
Card Content
</motion.div>
);
}
```
## Page Transitions
```typescript
// components/PageTransition.tsx
"use client";
import { motion, AnimatePresence } from "framer-motion";
import { usePathname } from "next/navigation";
export function PageTransition({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
return (
<AnimatePresence mode="wait">
<motion.div
key={pathname}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
</AnimatePresence>
);
}
```
This Framer Motion guide covers variants, layout animations, scroll effects, gestures, and page transitions.This framer-motion prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their framer-motion implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For framer-motion projects, consider mentioning your framework version, coding style, and any specific libraries you're using.