React animations can take your web apps to the next level, and Framer Motion is a fantastic library to make that happen. I’ve been using it for a while now, and let me tell you, it’s a game-changer.
First things first, you’ll need to install Framer Motion. It’s as simple as running:
npm install framer-motion
Once you’ve got it installed, you can start adding some slick animations to your components. Let’s kick things off with a basic example:
import { motion } from 'framer-motion';
const AnimatedBox = () => (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
>
Hello, animated world!
</motion.div>
);
This creates a div that fades in over one second. Pretty cool, right? But we’re just scratching the surface here.
One of the things I love about Framer Motion is how easy it makes creating complex animations. Let’s say you want to create a button that grows and changes color when you hover over it:
import { motion } from 'framer-motion';
const AnimatedButton = () => (
<motion.button
whileHover={{
scale: 1.1,
backgroundColor: '#ff0000',
}}
transition={{ duration: 0.3 }}
>
Click me!
</motion.button>
);
This button will smoothly scale up and turn red when you hover over it. It’s these little touches that can really make your UI feel alive and responsive.
But what if you want to animate multiple elements at once? Framer Motion has you covered with its AnimatePresence
component. This is particularly useful for animating elements as they enter or leave the DOM:
import { motion, AnimatePresence } from 'framer-motion';
import { useState } from 'react';
const AnimatedList = () => {
const [items, setItems] = useState([1, 2, 3]);
const removeItem = (item) => {
setItems(items.filter((i) => i !== item));
};
return (
<ul>
<AnimatePresence>
{items.map((item) => (
<motion.li
key={item}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.5 }}
onClick={() => removeItem(item)}
>
Item {item}
</motion.li>
))}
</AnimatePresence>
</ul>
);
};
In this example, each list item animates in when it’s added and animates out when it’s removed. It’s a great way to make your lists feel more dynamic and engaging.
Now, let’s talk about gestures. Framer Motion makes it super easy to add drag functionality to your components. Check this out:
import { motion } from 'framer-motion';
const DraggableBox = () => (
<motion.div
drag
dragConstraints={{
top: -50,
left: -50,
right: 50,
bottom: 50,
}}
style={{
width: 100,
height: 100,
background: 'blue',
}}
/>
);
This creates a blue box that you can drag around, but it’s constrained to a certain area. It’s like magic, but it’s just a few lines of code!
One of the coolest features of Framer Motion is the ability to create shared layout animations. This is when elements smoothly transition between different states, even when their layout changes. Here’s a simple example:
import { motion, AnimateSharedLayout } from 'framer-motion';
import { useState } from 'react';
const SharedLayoutAnimation = () => {
const [selected, setSelected] = useState(null);
return (
<AnimateSharedLayout>
<ul>
{['A', 'B', 'C'].map((item) => (
<motion.li
key={item}
layoutId={item}
onClick={() => setSelected(item)}
style={{
background: item === selected ? 'red' : 'blue',
padding: '10px',
margin: '10px',
cursor: 'pointer',
}}
>
{item}
</motion.li>
))}
</ul>
</AnimateSharedLayout>
);
};
In this example, when you click on an item, it smoothly transitions to its new state. It’s a great way to create a sense of continuity in your UI.
Now, let’s talk about something a bit more advanced: SVG animations. Framer Motion works beautifully with SVGs, allowing you to create some truly eye-catching effects. Here’s an example of an animated progress circle:
import { motion } from 'framer-motion';
const ProgressCircle = ({ progress }) => (
<svg width="100" height="100">
<motion.circle
cx="50"
cy="50"
r="40"
stroke="blue"
strokeWidth="4"
fill="transparent"
initial={{ pathLength: 0 }}
animate={{ pathLength: progress }}
transition={{ duration: 1 }}
/>
</svg>
);
This creates a circle that fills up based on the progress prop. It’s a great way to visualize loading or completion states.
One thing I’ve found really useful is Framer Motion’s ability to create custom animations. You’re not limited to just the built-in transitions. You can create your own easing functions for truly unique effects:
import { motion, useAnimation } from 'framer-motion';
const customEasing = [0.6, -0.05, 0.01, 0.99];
const CustomAnimation = () => {
const controls = useAnimation();
const handleClick = async () => {
await controls.start({
scale: [1, 2, 2, 1, 1],
rotate: [0, 0, 270, 270, 0],
borderRadius: ["20%", "20%", "50%", "50%", "20%"],
transition: { duration: 2, ease: customEasing }
});
};
return (
<motion.div
animate={controls}
onClick={handleClick}
style={{
width: 100,
height: 100,
background: 'purple',
}}
/>
);
};
This creates a purple square that goes through a complex animation when clicked. It’s a great way to add some personality to your UI elements.
Another powerful feature of Framer Motion is the ability to create spring animations. These can add a natural, physics-based feel to your animations:
import { motion } from 'framer-motion';
const SpringAnimation = () => (
<motion.div
initial={{ x: -100 }}
animate={{ x: 0 }}
transition={{
type: 'spring',
stiffness: 120,
damping: 20
}}
style={{
width: 100,
height: 100,
background: 'green',
}}
/>
);
This creates a green box that springs into view from the left. You can adjust the stiffness and damping to get the exact feel you want.
Now, let’s talk about orchestrating complex animations. Sometimes you want multiple elements to animate in sequence. Framer Motion makes this easy with its variants
feature:
import { motion } from 'framer-motion';
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
when: 'beforeChildren',
staggerChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
},
};
const StaggeredAnimation = () => (
<motion.ul
variants={containerVariants}
initial="hidden"
animate="visible"
>
{['Item 1', 'Item 2', 'Item 3'].map((item) => (
<motion.li key={item} variants={itemVariants}>
{item}
</motion.li>
))}
</motion.ul>
);
This creates a list where the items animate in one after another. It’s a great way to draw attention to a series of elements.
One of the things I’ve found most useful about Framer Motion is how well it integrates with React’s state management. You can easily tie your animations to your component’s state:
import { motion } from 'framer-motion';
import { useState } from 'react';
const ToggleAnimation = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.div
animate={{
rotateX: isOpen ? 180 : 0,
backgroundColor: isOpen ? '#ff0000' : '#0000ff',
}}
transition={{ duration: 0.5 }}
onClick={() => setIsOpen(!isOpen)}
style={{
width: 100,
height: 100,
}}
/>
);
};
This creates a box that flips and changes color when clicked. It’s a simple example, but it shows how you can create interactive, state-driven animations.
Framer Motion also provides a way to create scroll-triggered animations. This can be great for creating engaging landing pages or long-form content:
import { motion, useViewportScroll, useTransform } from 'framer-motion';
const ScrollAnimation = () => {
const { scrollYProgress } = useViewportScroll();
const scale = useTransform(scrollYProgress, [0, 1], [0.2, 2]);
return (
<motion.div
style={{
scale,
width: 100,
height: 100,
background: 'orange',
}}
/>
);
};
This creates an orange box that grows as you scroll down the page. It’s a simple effect, but it can really make your site feel dynamic and responsive.
Lastly, let’s talk about exit animations. These are crucial for creating smooth transitions when removing elements from the DOM. Here’s an example:
import { motion, AnimatePresence } from 'framer-motion';
import { useState } from 'react';
const ExitAnimation = () => {
const [isVisible, setIsVisible] = useState(true);
return (
<>
<button onClick={() => setIsVisible(!isVisible)}>Toggle</button>
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
style={{
width: 100,
height: 100,
background: 'pink',
}}
/>
)}
</AnimatePresence>
</>
);
};
This creates a pink box that fades in and out when you toggle it. The AnimatePresence
component ensures that the exit animation plays before the element is removed from the DOM.
In conclusion, Framer Motion is an incredibly powerful tool for adding animations to your React applications. From simple fades to complex, orchestrated animations, it provides an intuitive API that makes it easy to bring your UI to life. Remember, the key to great animations is subtlety - use them to enhance your user experience, not overwhelm it. Happy animating!