[FEATURE] Apply `staggerChildren` to components entering into an active variant
1. Describe the bug
Children components wrapped in AnimatePresence don't stagger as they should if they are not rendered initially. In provided example, you can see a comparison between child components mapped initially from static data array (works fine) and rendered conditionally, e.g. after some remote data is fetched, which is a common scenario.
2. CodeSandbox reproduction of the bug https://codesandbox.io/s/staggerchildrenissue-tkkie?file=/src/App.js
3. Steps to reproduce Refresh provided codesandbox example.
4. Expected behavior Children components rendered conditionally should be staggered the same as the ones rendering initially.
5. Environment details KDE neon 5.19, Chrome 85.0.4183.102 (Official Build) (64-bit)
staggerChildren currently only works when the parent variant actually changes, though I can see how this would be useful.
Is there a workaround for this use case? I think this is a very common case.
Any resolution on this ?
It makes no sense if it's not able to perform on the dynamic data.
Facing the same issue as well – for some reason dynamically fetched data (from localStorage, with useSWR) don't stagger, even when I set a unique key for each of the list items 😕
Edit: Inspired by @mattgperry's comment above, I figured out the following workaround – by setting a key on the parent element to the length of the dynamic data, something like this:
<motion.ul
key={data.length}
initial="hidden"
animate="show"
variants={{
hidden: {},
show: {
transition: {
staggerChildren: 0.1,
},
},
}}
>
{data.map((d) => (
<motion.li
variants={variants}
>
{d.name}
</motion.li>
)}
</motion.ul>
Edit: Inspired by @mattgperry's comment above, I figured out the following workaround – by setting a key on the parent element to the length of the dynamic data, something like this:
Yes but that only helps if the parent did not have their own transitions. A key change would reset this transition.
The use of this feature is limited if it did not work if children get added after the parent transition start.
I have made a small Sandbox for demonstration for my ticket: Example Toggle Demo1: Expected behavior Toggle Demo2: Example with children added after timeout
Same issue here! I need to load a list of items dynamically with a "load more" button... This is my workaround:
const itemVariants = (index) => ({
initial: {
opacity: 0,
y: -50 + index * 5,
},
animate: {
opacity: 1,
y: 0,
transition: {
delay: 0.025 * index,
},
},
});
<motion.div initial="initial" animate="animate" exit="initial">
{items.map((item, index) => (
<motion.span
key={item.id}
variants={itemVariants(index)}
>
Item #{index}
</motion.span>
))}
</motion.div>
Facing the same issue as well – for some reason dynamically fetched data (from localStorage, with
useSWR) don't stagger, even when I set a unique key for each of the list items 😕Edit: Inspired by @mattgperry's comment above, I figured out the following workaround – by setting a key on the parent element to the length of the dynamic data, something like this:
<motion.ul key={data.length} initial="hidden" animate="show" variants={{ hidden: {}, show: { transition: { staggerChildren: 0.1, }, }, }} > {data.map((d) => ( <motion.li variants={variants} > {d.name} </motion.li> )} </motion.ul>
Thank for this workaround 🥇. I almost uninstall framer-motion 😆
Hello, I try for my project the @steven-tey solution works fine. But if u want to dynamically delete some elements, it gonna re-render the container and the animations. So this solutions isn't good at 100%. As you can see on my short video : https://github.com/user-attachments/assets/f80e1ee5-2452-4bcd-85c5-bcc593b582c1 (sorry for the bad quality)
Regard, Axel