Staggered animations are a popular technique for creating dynamic, visually engaging effects where multiple elements animate sequentially, rather than all at once. This technique is commonly used for list items, modals, and other groupings of elements. React Spring, a popular animation library for React, provides a straightforward way to implement staggered animations with hooks like useTrail
and useSprings
.
In this guide, we’ll walk through how to create staggered animations in lists using React Spring, so that each item animates with a slight delay relative to the others.
1. Setting Up the Project
To get started, make sure you have a React project. If not, create one:
npx create-react-app staggered-animations
cd staggered-animations
Then, install React Spring:
npm install @react-spring/web
2. Basic Staggered Animation Using useTrail
React Spring provides the useTrail
hook for creating staggered animations. It allows you to animate multiple elements in a list with a delay between each element.
Example: Staggered Fade-In List
import React from 'react';
import { useTrail, animated } from '@react-spring/web';
const StaggeredList = () => {
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
// Create the staggered animation
const trail = useTrail(items.length, {
opacity: 1,
transform: 'translateY(0)',
from: { opacity: 0, transform: 'translateY(50px)' },
config: { mass: 1, tension: 180, friction: 20 },
delay: 200, // Optional delay before starting animation
});
return (
<div>
{trail.map((style, index) => (
<animated.div key={index} style={style} className="list-item">
{items[index]}
</animated.div>
))}
</div>
);
};
export default StaggeredList;
Explanation:
useTrail
: This hook generates a list of animated styles for multiple items. The first parameter is the number of items, and the second parameter is an object defining the animation properties (opacity
,transform
, etc.).trail.map
: This iterates over thetrail
array (which contains the animated styles) and applies them to each item.from
andto
: The initial and final states for the animation. In this case, each list item starts with 0 opacity and is translated 50px down, and then it fades in and slides up to its final position.
3. Customizing the Staggered Animation with Delays
You can customize the staggering effect by controlling the delay
and duration
of the animations.
Example: Delay Between List Items
import React from 'react';
import { useTrail, animated } from '@react-spring/web';
const StaggeredList = () => {
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
// Create the staggered animation with a delay
const trail = useTrail(items.length, {
opacity: 1,
transform: 'translateY(0)',
from: { opacity: 0, transform: 'translateY(50px)' },
delay: 200, // 200ms delay before starting animation
config: { mass: 1, tension: 170, friction: 26 },
});
return (
<div>
{trail.map((style, index) => (
<animated.div key={index} style={style} className="list-item">
{items[index]}
</animated.div>
))}
</div>
);
};
export default StaggeredList;
Here, the delay
property adds a delay before the entire animation starts. You can also control the duration by adjusting the config
settings like tension
and friction
.
4. Staggered Animations with Dynamic Data
If your list of items is dynamic, you can apply staggered animations to any collection of data, like an API response or user-generated content.
Example: Staggered List with Dynamic Items
import React, { useState } from 'react';
import { useTrail, animated } from '@react-spring/web';
const DynamicStaggeredList = () => {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
const addItem = () => {
setItems([...items, `Item ${items.length + 1}`]);
};
const trail = useTrail(items.length, {
opacity: 1,
transform: 'translateY(0)',
from: { opacity: 0, transform: 'translateY(50px)' },
config: { mass: 1, tension: 180, friction: 20 },
});
return (
<div>
<button onClick={addItem}>Add Item</button>
<div>
{trail.map((style, index) => (
<animated.div key={index} style={style} className="list-item">
{items[index]}
</animated.div>
))}
</div>
</div>
);
};
export default DynamicStaggeredList;
Explanation:
- Dynamic Data: The list starts with a few items, and more items can be added by clicking the “Add Item” button. React Spring automatically handles the animation for new items as they are added.
useTrail
with dynamic list: Even when the list changes dynamically, React Spring ensures the staggered animation still works, animating the new items sequentially.
5. Styling the List Items
You can add some basic CSS to style the list and give it a cleaner appearance.
/* App.css */
.list-item {
background-color: #f0f0f0;
padding: 10px;
margin: 5px 0;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
button {
padding: 10px;
margin: 10px 0;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
6. Adding More Advanced Staggering with useSprings
While useTrail
is great for simpler staggered animations, for more control over each element’s animation (e.g., different delays, animations for each item), you can use the useSprings
hook.
Example: Staggered Animation with useSprings
import React from 'react';
import { useSprings, animated } from '@react-spring/web';
const StaggeredListWithSprings = () => {
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
const springs = useSprings(
items.length,
items.map((_, index) => ({
opacity: 1,
transform: 'translateY(0)',
from: { opacity: 0, transform: 'translateY(50px)' },
delay: index * 100, // Increase delay for each item
config: { tension: 180, friction: 26 },
}))
);
return (
<div>
{springs.map((style, index) => (
<animated.div key={index} style={style} className="list-item">
{items[index]}
</animated.div>
))}
</div>
);
};
export default StaggeredListWithSprings;
Explanation:
useSprings
: This hook is used for animating multiple elements, and in this case, each item gets a staggered delay based on its index (index * 100
).springs
: The result is an array of animated styles that correspond to each item in the list, with each element having its own delay.