Motion Components & Props: Animate, Initial, and Exit States
Motion components in Framer Motion are the foundation of declarative animation. A motion component wraps standard HTML or SVG elements and accepts props that define animation states: initial (starting), animate (target), and exit (on unmount). Together, these three states create the complete lifecycle of a motion effect.
I've animated hundreds of React components using Framer Motion, and the pattern of initial-animate-exit is surprisingly powerful once you invert your thinking: instead of writing imperative animation code, you describe what each state looks like and let Framer Motion handle the transitions between them.
How Motion Component Props Work
Every motion component follows the same pattern: you define states as objects containing CSS properties, and Framer Motion interpolates between them. Here's the complete lifecycle:
import { motion } from 'framer-motion';
export function CardAnimation() {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.8, y: -20 }}
transition={{ duration: 0.6 }}
style={{
padding: '20px',
backgroundColor: '#f0f0f0',
borderRadius: '8px',
width: '300px'
}}
>
<h3>Card Content</h3>
<p>Mounts with opacity fade-in, scale-up, and upward slide.</p>
</motion.div>
);
}
When the component mounts, Framer Motion renders the initial state (opacity 0, scale 0.8, y offset 20px) and immediately animates toward animate (opacity 1, scale 1, y 0). If the component unmounts and is wrapped by AnimatePresence, it animates through the exit state before removing from the DOM.
The initial Prop: Defining Starting State
The initial prop sets the starting appearance before animation begins. This is applied the instant the component mounts, before the animate transition starts.
import { motion } from 'framer-motion';
export function FadeInFromRight() {
return (
<motion.div
initial={{ opacity: 0, x: 100 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.7 }}
style={{
padding: '15px',
backgroundColor: '#007bff',
color: '#fff',
borderRadius: '4px'
}}
>
This box fades in from the right.
</motion.div>
);
}
The initial state is crucial for perceived performance: without it, the component renders at the target state (fully visible) and then animates backward, which feels wrong. By setting initial to the off-screen, transparent state, the animation plays forward naturally from entry.
The animate Prop: Target Animation State
The animate prop defines the final state after the transition completes. Framer Motion animates all properties from initial to animate using the transition configuration.
import { motion } from 'framer-motion';
export function ButtonHoverExample() {
const [isHovered, setIsHovered] = React.useState(false);
return (
<motion.button
animate={{
backgroundColor: isHovered ? '#ff6b6b' : '#3498db',
scale: isHovered ? 1.05 : 1,
boxShadow: isHovered
? '0 10px 20px rgba(0,0,0,0.2)'
: '0 4px 6px rgba(0,0,0,0.1)'
}}
transition={{ duration: 0.3 }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{
padding: '10px 20px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontWeight: 'bold'
}}
>
Hover Me
</motion.button>
);
}
In this example, animate is dynamic: it changes based on the isHovered state. When isHovered is true, the button animates to red, larger scale, and a bigger shadow. Framer Motion re-targets the animation automatically when the animate prop changes.
The exit Prop: Unmount Animations
The exit prop defines the animation that plays when a component is removed from the DOM. This only works when the motion component is wrapped by AnimatePresence, which delays unmounting until the exit animation completes.
import { motion, AnimatePresence } from 'framer-motion';
import React from 'react';
export function ToastNotification() {
const [showToast, setShowToast] = React.useState(true);
return (
<div>
<button onClick={() => setShowToast(!showToast)}>
{showToast ? 'Hide' : 'Show'} Toast
</button>
<AnimatePresence>
{showToast && (
<motion.div
initial={{ opacity: 0, y: -30 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 30 }}
transition={{ duration: 0.4 }}
style={{
position: 'fixed',
top: '20px',
right: '20px',
padding: '15px 20px',
backgroundColor: '#28a745',
color: '#fff',
borderRadius: '4px',
boxShadow: '0 4px 12px rgba(0,0,0,0.2)'
}}
>
Success! Your changes were saved.
</motion.div>
)}
</AnimatePresence>
</div>
);
}
Without exit, the toast would disappear instantly. With it, the toast slides down and fades out before unmounting. The AnimatePresence component is required for exit animations to work—it's a utility that keeps motion components in the DOM long enough for their exit animations to play.
Common Animatable Properties
Framer Motion supports a wide range of CSS properties and custom transforms:
| Property | Type | Example |
|---|---|---|
opacity | number (0-1) | { opacity: 0.5 } |
x, y | pixels or % | { x: 100, y: -50 } |
scale | number | { scale: 1.5 } |
rotate | degrees | { rotate: 45 } |
skew | degrees | { skewX: 10 } |
color, backgroundColor | CSS color | { backgroundColor: '#ff0000' } |
borderRadius | pixels | { borderRadius: '50%' } |
filter | blur/brightness | { filter: 'blur(5px)' } |
Any CSS property that uses interpolable values (numbers, colors, lengths) can be animated. Framer Motion intelligently detects the property type and animates accordingly.
Using Variables for Reusable States
For complex animations used in multiple places, define state objects as variables:
import { motion } from 'framer-motion';
const fadeInVariant = {
initial: { opacity: 0 },
animate: { opacity: 1 },
exit: { opacity: 0 }
};
const slideInVariant = {
initial: { x: -50, opacity: 0 },
animate: { x: 0, opacity: 1 },
exit: { x: -50, opacity: 0 }
};
export function ListItem({ item }) {
return (
<motion.li
variants={slideInVariant}
transition={{ duration: 0.5 }}
style={{ padding: '10px', borderBottom: '1px solid #eee' }}
>
{item.name}
</motion.li>
);
}
Here we define animation states as objects and pass them to variants instead of initial, animate, exit. This is a best practice for reusable animation patterns—you define the shape once and apply it to multiple components.
Key Takeaways
- Motion component props define animation states:
initial(start),animate(target),exit(unmount). - Every property in these objects is interpolated smoothly by Framer Motion over the
transitionduration. - The
exitprop only works insideAnimatePresence, which delays unmounting to allow exit animations to complete. - Dynamic
animatevalues (tied to state) allow interactive animations that respond to user input. - Define reusable animation patterns as variant objects and pass them to the
variantsprop.
Frequently Asked Questions
What happens if I don't define an initial prop?
If initial is omitted, Framer Motion uses the current CSS values or defaults. The component renders at the target animate state, then animates backward if the state changes. Best practice is to always define initial to avoid invisible animations.
Can I animate custom CSS properties (CSS variables)?
Yes. Framer Motion can animate custom properties like --my-color if they use interpolable values (numbers, colors). Define them like { '--my-color': '#ff0000' } and ensure your CSS references them.
How do I animate multiple elements in sequence with exit?
Use the exit prop on each motion component inside AnimatePresence. Combine with transition.delayChildren and transition.staggerChildren to create a choreographed exit (see the Variants article for details).
Do I need AnimatePresence for animate to work?
No. AnimatePresence is only required for exit animations. The initial and animate props work on their own when the component mounts.