Framer Motion
Production-ready animation library for React and Next.js
✨ What is Framer Motion?
Framer Motion is a powerful animation library for React that makes it easy to create smooth, professional animations. It provides simple APIs for complex animations, gestures, and page transitions in Next.js applications.
# Install Framer Motion
npm install framer-motion
# Import and use in components
import { motion } from 'framer-motion'
Key Framer Motion Features
Animations
Smooth declarative animations
<motion.div
animate={{ x: 100 }}
/>
Gestures
Drag, hover, and tap interactions
<motion.div
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
/>
Transitions
Page and route transitions
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
/>
Variants
Reusable animation states
const variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 }
}
🔹 Basic Animation
Create simple animations by replacing HTML elements with motion components. The animate prop defines the final state, and Framer Motion automatically animates from the current state.
// components/FadeIn.js
'use client'
import { motion } from 'framer-motion'
export default function FadeIn() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="p-6 bg-blue-500 text-white rounded-lg"
>
<h2>Hello, I fade in!</h2>
<p>This component animates when it appears.</p>
</motion.div>
)
}
Animation Effect:
The div fades in from opacity 0 to 1 and moves up 20px over 0.5 seconds.
🔹 Hover and Tap Animations
Add interactive animations that respond to user actions. These gesture-based animations provide immediate feedback and make your interface feel more responsive and engaging.
// components/InteractiveButton.js
'use client'
import { motion } from 'framer-motion'
export default function InteractiveButton() {
return (
<motion.button
whileHover={{
scale: 1.05,
boxShadow: "0px 5px 15px rgba(0, 0, 0, 0.2)"
}}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
className="px-6 py-3 bg-purple-500 text-white rounded-lg"
>
Hover and Click Me!
</motion.button>
)
}
🔹 Variants for Complex Animations
Define animation states using variants for cleaner code and coordinated animations. Variants make it easy to orchestrate animations across multiple elements and create consistent motion patterns.
// components/AnimatedList.js
'use client'
import { motion } from 'framer-motion'
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
}
const itemVariants = {
hidden: { opacity: 0, x: -20 },
visible: { opacity: 1, x: 0 }
}
export default function AnimatedList() {
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4']
return (
<motion.ul
variants={containerVariants}
initial="hidden"
animate="visible"
className="space-y-2"
>
{items.map((item, index) => (
<motion.li
key={index}
variants={itemVariants}
className="p-4 bg-gray-100 rounded"
>
{item}
</motion.li>
))}
</motion.ul>
)
}
🔹 Drag Interactions
Enable drag functionality with simple props. Framer Motion handles all the complex physics and constraints, making it easy to create draggable elements with natural motion.
// components/DraggableBox.js
'use client'
import { motion } from 'framer-motion'
export default function DraggableBox() {
return (
<div className="h-64 bg-gray-100 rounded-lg relative">
<motion.div
drag
dragConstraints={{
top: 0,
left: 0,
right: 200,
bottom: 200
}}
dragElastic={0.2}
whileDrag={{ scale: 1.1 }}
className="w-20 h-20 bg-blue-500 rounded-lg cursor-grab active:cursor-grabbing"
>
<p className="text-white text-center leading-20">Drag me!</p>
</motion.div>
</div>
)
}
🔹 Page Transitions
Create smooth transitions between pages in your Next.js app. AnimatePresence allows components to animate out when they're removed from the React tree.
// app/layout.js
'use client'
import { AnimatePresence, motion } from 'framer-motion'
import { usePathname } from 'next/navigation'
export default function RootLayout({ children }) {
const pathname = usePathname()
return (
<html lang="en">
<body>
<AnimatePresence mode="wait">
<motion.div
key={pathname}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
</AnimatePresence>
</body>
</html>
)
}
🔹 Scroll Animations
Trigger animations based on scroll position using useScroll and useTransform hooks. Create parallax effects, progress bars, and elements that animate as users scroll.
// components/ScrollReveal.js
'use client'
import { motion, useScroll, useTransform } from 'framer-motion'
import { useRef } from 'react'
export default function ScrollReveal() {
const ref = useRef(null)
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end start"]
})
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 (
<motion.div
ref={ref}
style={{ opacity, scale }}
className="p-8 bg-gradient-to-r from-purple-500 to-pink-500 text-white rounded-lg"
>
<h2 className="text-2xl font-bold">Scroll to reveal!</h2>
<p>This element animates as you scroll.</p>
</motion.div>
)
}
🔹 Keyframe Animations
Create complex multi-step animations using arrays of values. Keyframes allow you to define multiple animation states that play in sequence.
// components/BouncingBall.js
'use client'
import { motion } from 'framer-motion'
export default function BouncingBall() {
return (
<motion.div
animate={{
y: [0, -100, 0],
scale: [1, 1.2, 1],
rotate: [0, 180, 360]
}}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut"
}}
className="w-16 h-16 bg-red-500 rounded-full"
/>
)
}
🔹 Layout Animations
Automatically animate layout changes with the layout prop. Framer Motion smoothly transitions between different layouts, sizes, and positions without manual calculations.
// components/ExpandableCard.js
'use client'
import { motion } from 'framer-motion'
import { useState } from 'react'
export default function ExpandableCard() {
const [isExpanded, setIsExpanded] = useState(false)
return (
<motion.div
layout
onClick={() => setIsExpanded(!isExpanded)}
className="p-6 bg-white rounded-lg shadow-lg cursor-pointer"
style={{ width: isExpanded ? '400px' : '200px' }}
>
<motion.h2 layout className="text-xl font-bold">
Click to expand
</motion.h2>
{isExpanded && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="mt-4"
>
This content appears when expanded!
</motion.p>
)}
</motion.div>
)
}