- Getting Started with Framer Motion
- Understanding Key Concepts in Framer Motion
- Creating Advanced Animations
- Advanced Techniques in Framer Motion
- Combining Animations for Complex Effects
- Performance Optimization in Framer Motion
- Framer Motion with React Hooks
- Real-World Examples and Best Practices
- Interactive Animations with Framer Motion
- Combining React State and Framer Motion
- Enhancing User Interactions with Framer Motion
- Best Practices for Using Framer Motion
- Conclusion
Animating your React applications can make them more engaging and user-friendly. Framer Motion is a powerful library that simplifies the process of adding advanced animations to your React projects. This article will walk you through everything you need to know about using Framer Motion for advanced animations, from the basics to more complex animations.
Getting Started with Framer Motion
Framer Motion is an open-source library that provides a simple and powerful API for creating animations in React. It’s built on top of the Framer library and uses the same principles of animation but is specifically designed for React applications. To get started with Framer Motion, you first need to install it in your React project.
Installing Framer Motion
To install Framer Motion, you can use npm or yarn. Run the following command in your terminal:
npm install framer-motion
or
yarn add framer-motion
Once installed, you can import it into your React components and start using it to animate your elements.
Basic Usage of Framer Motion
Framer Motion provides a motion
component that you can use to wrap any element you want to animate. Here’s a simple example:
import { motion } from 'framer-motion';
function App() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1 }}
>
Hello, Framer Motion!
</motion.div>
);
}
export default App;
In this example, the motion.div
element will fade in from an opacity of 0 to an opacity of 1 over the course of one second.
Understanding Key Concepts in Framer Motion
Before diving into more complex animations, it’s important to understand some key concepts in Framer Motion: initial
, animate
, transition
, and variants
.
Initial and Animate
The initial
prop sets the starting state of the animation, while the animate
prop defines the end state. These props can accept an object of styles you want to animate.
<motion.div
initial={{ opacity: 0, x: -100 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
Slide in from the left
</motion.div>
In this example, the element will slide in from the left while fading in.
Transition
The transition
prop controls the timing and easing of the animation. You can specify the duration
, delay
, ease
, and more.
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ type: 'spring', stiffness: 260, damping: 20 }}
>
Bouncing animation
</motion.div>
This example uses a spring transition for a more natural, bouncy effect.
Variants
Variants allow you to define multiple animation states and switch between them. This is useful for more complex animations.
const variants = {
hidden: { opacity: 0, y: -100 },
visible: { opacity: 1, y: 0 },
};
<motion.div
initial="hidden"
animate="visible"
variants={variants}
transition={{ duration: 0.5 }}
>
Animate with variants
</motion.div>
Using variants can make your code cleaner and more manageable.
Creating Advanced Animations
With the basics covered, let’s move on to creating more advanced animations. We’ll explore staggered animations, keyframes, and using gestures to trigger animations.
Staggered Animations
Staggered animations involve animating multiple elements one after the other with a delay. This can create a cascading effect.
const containerVariants = {
hidden: { opacity: 1 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.3,
},
},
};
const childVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
<motion.div
className="container"
variants={containerVariants}
initial="hidden"
animate="visible"
>
<motion.div className="child" variants={childVariants} />
<motion.div className="child" variants={childVariants} />
<motion.div className="child" variants={childVariants} />
</motion.div>
In this example, each child element will animate in one after the other with a delay of 0.3 seconds.
Keyframes
Keyframes allow you to define multiple stages of an animation. This can create more complex and interesting effects.
<motion.div
animate={{
x: [0, 100, 50, 100, 0],
opacity: [1, 0.5, 1, 0.5, 1],
}}
transition={{ duration: 2 }}
>
Keyframe animation
</motion.div>
In this example, the element will move to different positions and change opacity at each stage of the animation.
Gesture-Based Animations
Framer Motion also supports gesture-based animations, such as dragging, hovering, and tapping.
<motion.div
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.8 }}
drag
dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
>
Drag me!
</motion.div>
This example scales the element when hovered and tapped, and allows it to be dragged within specified constraints.
Advanced Techniques in Framer Motion
Now that we have a solid grasp of the basics, let’s delve into more advanced techniques. These techniques include animating presence, using layout animations, and creating custom animations with hooks.
Animating Presence
Animating the presence of elements, such as adding or removing elements from the DOM, can greatly enhance user experience. Framer Motion provides a AnimatePresence
component that makes this easy.
import { AnimatePresence, motion } from 'framer-motion';
import { useState } from 'react';
function App() {
const [isVisible, setIsVisible] = useState(true);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>Toggle</button>
<AnimatePresence>
{isVisible && (
<motion.div
key="box"
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 50 }}
transition={{ duration: 0.5 }}
>
Animated Box
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default App;
In this example, the motion.div
element animates in and out of the DOM with smooth transitions, controlled by the AnimatePresence
component.
Layout Animations
Layout animations allow elements to animate their position when their layout changes. This can be particularly useful for animating lists or grids of items.
const items = ['Item 1', 'Item 2', 'Item 3'];
function List() {
return (
<div>
{items.map((item, index) => (
<motion.div
key={item}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{item}
</motion.div>
))}
</div>
);
}
export default List;
In this example, the layout
prop is used to animate the position of the list items when they change.
Custom Animations with Hooks
Framer Motion provides several hooks that allow you to create custom animations. The most commonly used hooks are useAnimation
and useCycle
.
useAnimation
The useAnimation
hook gives you full control over your animations, allowing you to start, stop, and update animations programmatically.
import { useAnimation } from 'framer-motion';
import { useEffect } from 'react';
function CustomAnimation() {
const controls = useAnimation();
useEffect(() => {
controls.start({
x: 100,
transition: { duration: 1 },
});
}, [controls]);
return <motion.div animate={controls}>Custom Animation</motion.div>;
}
export default CustomAnimation;
In this example, the useEffect
hook starts the animation when the component mounts.
useCycle
The useCycle
hook allows you to cycle through different animation states. This can be useful for toggling between different animations.
import { useCycle } from 'framer-motion';
function ToggleAnimation() {
const [animate, cycle] = useCycle(
{ scale: 1, rotate: 0 },
{ scale: 1.5, rotate: 180 }
);
return (
<motion.div
animate={animate}
onClick={() => cycle()}
transition={{ duration: 0.5 }}
>
Click to toggle
</motion.div>
);
}
export default ToggleAnimation;
In this example, clicking the element will toggle between two different animation states.
Combining Animations for Complex Effects
By combining various animations and techniques, you can create more complex and sophisticated effects. Let’s explore a few examples.
Animating a Modal
Animating a modal window can improve the user experience by providing smooth transitions when the modal appears and disappears.
import { AnimatePresence, motion } from 'framer-motion';
import { useState } from 'react';
function Modal() {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Modal</button>
<AnimatePresence>
{isOpen && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
className="modal"
>
<motion.div
initial={{ y: -100 }}
animate={{ y: 0 }}
exit={{ y: -100 }}
transition={{ duration: 0.5 }}
className="modal-content"
>
<button onClick={() => setIsOpen(false)}>Close</button>
<p>This is a modal</p>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default Modal;
In this example, both the modal background and content animate in and out of the view, creating a smooth opening and closing effect.
Page Transitions
Creating seamless page transitions can enhance the user experience by providing visual continuity as users navigate between different views or pages.
import { AnimatePresence, motion } from 'framer-motion';
import { BrowserRouter as Router, Route, Switch, useLocation } from 'react-router-dom';
function Page1() {
return <div>Page 1</div>;
}
function Page2() {
return <div>Page 2</div>;
}
function App() {
const location = useLocation();
return (
<AnimatePresence>
<Switch location={location} key={location.pathname}>
<Route exact path="/" component={Page1} />
<Route path="/page2" component={Page2} />
</Switch>
</AnimatePresence>
);
}
function Root() {
return (
<Router>
<App />
</Router>
);
}
export default Root;
In this example, the AnimatePresence
component is used to animate the transitions between different routes, creating a smooth page transition effect.
Performance Optimization in Framer Motion
As you integrate animations into your React applications, it’s essential to keep performance in mind. Poorly optimized animations can lead to a sluggish user experience. Here are some tips for optimizing performance when using Framer Motion.
Minimizing Repaints and Reflows
One of the primary ways to optimize performance is to minimize repaints and reflows. These are processes that the browser undergoes to render your animations, and they can be costly in terms of performance.
- Use Transform Properties: CSS properties like
transform
andopacity
are GPU-accelerated and do not trigger reflows. Prefer usingtranslate
,scale
,rotate
, andopacity
for your animations.
<motion.div
initial={{ transform: 'translateX(-100px)', opacity: 0 }}
animate={{ transform: 'translateX(0)', opacity: 1 }}
transition={{ duration: 0.5 }}
>
Efficient animation
</motion.div>
- Avoid Layout-Triggering Properties: Avoid animating properties that trigger layout changes, such as
width
,height
,margin
, andpadding
.
Throttling Animations
When dealing with animations that are triggered by user interactions, such as scroll or drag, it’s important to throttle the frequency of updates to avoid overwhelming the browser.
import { useMotionValue, useTransform, motion } from 'framer-motion';
import { useEffect } from 'react';
function ThrottledScrollAnimation() {
const scrollY = useMotionValue(0);
const y = useTransform(scrollY, [0, 300], [0, -100]);
useEffect(() => {
const handleScroll = () => {
scrollY.set(window.scrollY);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [scrollY]);
return <motion.div style={{ y }}>Throttled Scroll Animation</motion.div>;
}
export default ThrottledScrollAnimation;
In this example, useMotionValue
and useTransform
are used to create a throttled scroll animation.
Leveraging AnimatePresence
When animating elements entering or leaving the DOM, use the AnimatePresence
component to ensure that exiting animations complete before the element is removed from the DOM.
import { AnimatePresence, motion } from 'framer-motion';
import { useState } from 'react';
function PresenceOptimization() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
const removeItem = () => {
setItems(items.slice(0, -1));
};
return (
<div>
<button onClick={removeItem}>Remove Item</button>
<AnimatePresence>
{items.map((item, index) => (
<motion.div
key={item}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
>
{item}
</motion.div>
))}
</AnimatePresence>
</div>
);
}
export default PresenceOptimization;
Using AnimatePresence
ensures that the exit animation completes smoothly before the DOM updates.
Framer Motion with React Hooks
Combining Framer Motion with React hooks can enhance your ability to create dynamic and responsive animations.
useMotionValue and useTransform
useMotionValue
and useTransform
are powerful hooks that allow you to create responsive animations based on state or input values.
useMotionValue
useMotionValue
creates a motion value that can be used to drive animations.
import { useMotionValue, motion } from 'framer-motion';
function MotionValueExample() {
const x = useMotionValue(0);
return (
<motion.div
drag="x"
style={{ x }}
dragConstraints={{ left: -100, right: 100 }}
>
Drag me!
</motion.div>
);
}
export default MotionValueExample;
In this example, useMotionValue
is used to create a draggable element.
useTransform
useTransform
allows you to create derived values from a motion value. This can be used to create complex animations based on a single input.
import { useMotionValue, useTransform, motion } from 'framer-motion';
function TransformExample() {
const x = useMotionValue(0);
const scale = useTransform(x, [-100, 0, 100], [0.5, 1, 1.5]);
return (
<motion.div drag="x" style={{ x, scale }} dragConstraints={{ left: -100, right: 100 }}>
Transform me!
</motion.div>
);
}
export default TransformExample;
In this example, the scale
of the element is derived from its x
position, creating a dynamic scaling effect.
Real-World Examples and Best Practices
To wrap up our guide, let’s look at some real-world examples and best practices for using Framer Motion in production applications.
Creating a Responsive Navigation Menu
Responsive navigation menus are a common use case for animations. Here’s how to create a slide-in menu using Framer Motion.
import { motion } from 'framer-motion';
import { useState } from 'react';
function NavigationMenu() {
const [isOpen, setIsOpen] = useState(false);
return (
<nav>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Menu</button>
<motion.div
initial={{ x: '-100%' }}
animate={{ x: isOpen ? 0 : '-100%' }}
transition={{ type: 'spring', stiffness: 260, damping: 20 }}
className="menu"
>
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</motion.div>
</nav>
);
}
export default NavigationMenu;
In this example, the menu slides in and out of view when the toggle button is clicked.
Enhancing User Feedback with Animations
Animations can provide valuable feedback to users, making interactions feel more responsive and engaging.
import { motion } from 'framer-motion';
function FeedbackButton() {
return (
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
>
Click Me
</motion.button>
);
}
export default FeedbackButton;
This example uses animations to provide feedback when the button is hovered or clicked.
Best Practices
- Keep Animations Subtle: Overusing animations can overwhelm users. Keep animations subtle and purposeful.
- Ensure Accessibility: Make sure that animations do not interfere with accessibility. Provide options to disable animations for users who prefer reduced motion.
- Test Across Devices: Test your animations across different devices and browsers to ensure they perform well everywhere.
- Use Easing Functions: Use easing functions to create natural and smooth animations. Avoid linear animations as they can feel robotic.
Interactive Animations with Framer Motion
Adding interactivity to animations can significantly enhance user engagement. Framer Motion allows you to create interactive animations using gestures and events. Let’s explore some advanced interactive animations.
Parallax Effects
Parallax effects create an illusion of depth by moving background and foreground elements at different speeds. This effect can make your website more dynamic and engaging.
import { useMotionValue, useTransform, motion } from 'framer-motion';
import { useEffect } from 'react';
function ParallaxExample() {
const y = useMotionValue(0);
const y1 = useTransform(y, [0, 1], [0, -100]);
const y2 = useTransform(y, [0, 1], [0, -50]);
useEffect(() => {
const handleScroll = () => {
y.set(window.scrollY / document.body.scrollHeight);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [y]);
return (
<div>
<motion.div style={{ y: y1, background: 'blue', height: '50vh' }}>
Background
</motion.div>
<motion.div style={{ y: y2, background: 'green', height: '50vh' }}>
Foreground
</motion.div>
</div>
);
}
export default ParallaxExample;
In this example, the background and foreground elements move at different speeds as the user scrolls, creating a parallax effect.
Scroll-Based Animations
Scroll-based animations trigger changes in the UI as the user scrolls through the page. This can be used to animate elements into view or create other dynamic effects.
import { useViewportScroll, useTransform, motion } from 'framer-motion';
function ScrollAnimationExample() {
const { scrollYProgress } = useViewportScroll();
const scale = useTransform(scrollYProgress, [0, 1], [0.5, 1.5]);
return (
<motion.div style={{ scale, background: 'red', height: '100vh' }}>
Scroll to animate
</motion.div>
);
}
export default ScrollAnimationExample;
In this example, the scale of the element changes based on the scroll position, creating a dynamic scroll-based animation.
Combining React State and Framer Motion
Combining React state management with Framer Motion can create powerful interactive experiences. This allows you to animate elements based on user input or other state changes.
Animated Form Validation
Animating form validation feedback can make forms more user-friendly and engaging.
import { motion, AnimatePresence } from 'framer-motion';
import { useState } from 'react';
function FormValidationExample() {
const [inputValue, setInputValue] = useState('');
const [isValid, setIsValid] = useState(true);
const handleChange = (event) => {
const value = event.target.value;
setInputValue(value);
setIsValid(value.length >= 5);
};
return (
<div>
<input type="text" value={inputValue} onChange={handleChange} />
<AnimatePresence>
{!isValid && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 10 }}
transition={{ duration: 0.3 }}
style={{ color: 'red' }}
>
Input must be at least 5 characters
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default FormValidationExample;
In this example, validation feedback is animated, providing a smooth user experience.
Dynamic Loading Indicators
Dynamic loading indicators can improve user experience by providing visual feedback while data is being loaded.
import { motion } from 'framer-motion';
import { useState, useEffect } from 'react';
function LoadingIndicatorExample() {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => setIsLoading(false), 3000);
return () => clearTimeout(timer);
}, []);
return (
<div>
<AnimatePresence>
{isLoading && (
<motion.div
key="loading"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
style={{ background: 'yellow', padding: '10px' }}
>
Loading...
</motion.div>
)}
</AnimatePresence>
{!isLoading && <div>Data Loaded</div>}
</div>
);
}
export default LoadingIndicatorExample;
In this example, a loading indicator is animated in and out based on the loading state, providing clear feedback to the user.
Enhancing User Interactions with Framer Motion
Framer Motion can significantly enhance user interactions by providing responsive and intuitive animations. Let’s explore a few more use cases where Framer Motion can be utilized effectively.
Animated Tabs
Animated tabs can make navigation between different sections of your app smoother and more engaging.
import { motion } from 'framer-motion';
import { useState } from 'react';
function AnimatedTabs() {
const [selectedTab, setSelectedTab] = useState('tab1');
return (
<div>
<div style={{ display: 'flex' }}>
<button onClick={() => setSelectedTab('tab1')}>Tab 1</button>
<button onClick={() => setSelectedTab('tab2')}>Tab 2</button>
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
key={selectedTab}
transition={{ duration: 0.3 }}
>
{selectedTab === 'tab1' && <div>Content for Tab 1</div>}
{selectedTab === 'tab2' && <div>Content for Tab 2</div>}
</motion.div>
</div>
);
}
export default AnimatedTabs;
In this example, the content of the tabs is animated based on the selected tab, providing a smooth transition effect.
Animated Cards
Animating card components can make your UI more interactive and visually appealing.
import { motion } from 'framer-motion';
function AnimatedCard() {
return (
<motion.div
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
style={{
background: 'white',
padding: '20px',
borderRadius: '10px',
boxShadow: '0 5px 15px rgba(0, 0, 0, 0.1)',
}}
>
<h2>Card Title</h2>
<p>Card content goes here. This is an example of an animated card.</p>
</motion.div>
);
}
export default AnimatedCard;
In this example, the card component animates on hover and tap, providing a responsive and engaging user experience.
Best Practices for Using Framer Motion
To make the most out of Framer Motion, here are some best practices to follow:
Keep Animations Purposeful
Animations should enhance the user experience, not distract from it. Use animations to provide feedback, guide the user’s attention, or add a touch of delight. Avoid excessive or unnecessary animations.
Test on Multiple Devices
Ensure that your animations perform well across different devices and screen sizes. Test your animations on mobile, tablet, and desktop devices to ensure a smooth experience for all users.
Consider User Preferences
Some users may prefer reduced motion due to accessibility needs. Respect user preferences by providing options to disable or reduce animations. You can use the prefers-reduced-motion
media query to detect these preferences.
@media (prefers-reduced-motion: reduce) {
.animation {
animation: none;
}
}
Optimize for Performance
Animations should not compromise the performance of your application. Use performant CSS properties like transform
and opacity
, and minimize layout-triggering properties. Throttle animations triggered by user input to avoid performance bottlenecks.
Leverage Framer Motion’s Features
Framer Motion provides a wide range of features to create complex animations easily. Make use of AnimatePresence
for exit animations, useMotionValue
and useTransform
for dynamic animations, and variants
for reusable animation states.
Conclusion
Framer Motion is a versatile and powerful tool for adding advanced animations to your React applications. By understanding its core concepts and utilizing its features effectively, you can create engaging, responsive, and visually appealing user experiences.
From simple fade-ins to complex interactive animations, Framer Motion provides the tools you need to bring your React projects to life. Remember to keep animations purposeful, test across devices, consider user preferences, and optimize for performance to ensure the best possible experience for your users. Start experimenting with Framer Motion today and discover how it can transform your React applications into dynamic, animated masterpieces.
Read Next: