motion: [BUG] AnimatePresence doesn't update with the latest state if the state changes fast.

Describe the bug

AnimatePresence doesn’t update with the latest state if the state changes fast.

Provide a CodeSandbox reproduction of the bug

import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";

const ComponentWrapper = (props: any) => {
	const { status } = props;
	return (
		<motion.div
			initial={{ opacity: 1 }}
			animate={{ opacity: 1 }}
			exit={{ opacity: 0 }}
		>
			<h1>{status}</h1>
		</motion.div>
	);
};

function ApBug() {
	const [status, setStatus] = useState<number>(1);
	return (
		<div>
			<AnimatePresence mode="wait">
				<ComponentWrapper key={status} status={status} />
			</AnimatePresence>
			<h1>status: {status}</h1>
			<button
				onClick={() => {
					setStatus(status + 1);
				}}
			>
				+1
			</button>
		</div>
	);
}

export default ApBug;

CodeSandbox

Steps to reproduce

If the +1 button was clicked slowly, the animation will update with the state. While if the button was clicked swiftly, the animation stops updating with the state.

Expected behavior

The animation should update with the state.

Video or screenshots

If applicable, add a video or screenshots to help explain the bug.

https://user-images.githubusercontent.com/89364151/225664502-ac68c6cb-1979-418d-bde1-3710385d8049.mp4

Environment details

OS: Edition Windows 11 Pro Version 22H2 Installed on ‎11/‎10/‎2022 OS build 22621.1413 Experience Windows Feature Experience Pack 1000.22639.1000.0

Browser: Chrome Version 111.0.5563.65 (Official Build) (64-bit)

npm package versions:

{
  "name": "animation-sandbox",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "framer-motion": "^10.4.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.9.0",
    "styled-components": "^5.3.9"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@types/styled-components": "^5.1.26",
    "@vitejs/plugin-react": "^3.1.0",
    "typescript": "^4.9.5",
    "vite": "^4.2.0"
  }
}

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 41
  • Comments: 67 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Is there a concrete reason this issue has gone unresolved for so long? Phrased differently, is there a compelling reason not to expect a fix for this issue? (Intended behavior, unintended but necessary outcome of more important logic that cannot be changed without causing more global issues, phasing out of FM maintenance to work on paid Framer products, etc…)

FM hits the absolute sweet spot of intuitive API and powerful/performant anims. But this is increasingly feeling like a deal breaker - if same-page enter/exit transitions are employed with the ‘wait’ mode applied anyway. I understand this is open source and with this comes no expectations of fixes, speedy or otherwise. Writing mainly because I’d just really prefer sticking with FM as my main React anim library.

Any update on this?

Any update on this?

+1

Do you have a hint on how we could use this fork in a production env?

The patch should be reviewed and published by @mattgperry You can fork my fork and publish it on npm under @hatsumatsu/motion and install it via npm

I’ve got the exact same issue and found the regression version:

→ Works fine on 9.0.2 → Breaks on 9.0.3

This is a very important issue.

However, I have no idea what efforts Framer is making to fix this bug. I am not even sure if they are aware of this bug.

I would like to know the roadmap or plan to fix this bug.

10.15.2 still have bug.

I’m actually seeing this in a site running on 9.1.7 as well, so I’m not sure if this is a regression in v10 or just a long-standing bug?

There’s no reason that if the key is unique that nodes shouldn’t be getting removed/added properly, regardless of how fast changes are happening. If that’s not the case, that should be made clear in the docs 😬

Also noticed this bug. Using framer 10.16.4. Downgraded to 10.5.0 which partially solves the problem for me.

I have a Nextjs project that has a paginated list on a page. When fetching data for the next page I display a loading state. When the data is returned, I hide the loading state and show the data in a list.

I am using AnimatePresence with mode='wait' to nicely transition between the loading state and the list.

However, due to Nextjs caching, if I make a request that has already been made (e.g. went from page 1 to page 2, then back to page 1), the response is nearly immediate. Because this is so fast Framer doesn’t seem to pick up the state change and the loading state hangs indefinitely.

Downgrading to 10.5.0 at least eliminates the indefinite loading state issue, but there is still no animation when the response is too fast, resulting in a state change that is too fast.

@pofixifop are you sure that you are testing the right thing? That’s the codebox result with my patch:

I thought I was, but maybe I didn’t apply the patch correctly to my local branch? I thought I did as some other debug code I added was being called. I must apologize - I tried out your sandbox, and also tried copying the framer-motion.js file from there into Matthias’ code sandbox at https://codesandbox.io/s/framer-motion-animatepresence-buggy-rl8pd2?file=/src/App.tsx They both saw the issue being resolved!!!

@GianlucaGuarini I am so grateful for your help and work fixing this bug, and I’m sure many others will also be stoked that this issue (and a ton of duplicate issues related to it) can finally be put to rest. ☺️

Okay, let’s try help fix this.

Here is a minimal reproduction showing the issue: https://codesandbox.io/s/framer-motion-animatepresence-bug-w4ycgz?file=/src/App.js

Trying out different version of framer-motion , I can not confirm the 10.5.0 vs 10.6.0 theory. However @benjaminwaterlot 's 9.0.2 vs 9.0.3-alpha.0 theory seems to be correct.

There seems to be a refactor of the ExitAnimation component between these versions: https://github.com/framer/motion/compare/v9.0.2...v9.0.3-alpha.0

Anyone able to dig deep into this? 😇

10.13.0 still has the issue

10.12.22 still have bug.

Also for those still experiencing this, here’s a quick workaround that seems to work well:

<AnimatePresence mode="popLayout" initial={false}>
  // your unique motion component
</AnimatePresence>

And then add a delay to your animate transition that is >= to your exit transition.

Rapid changes do not experience the same degradation described in this thread 😎

I’m getting the same issue on the latest version of framer motion 10.6.1

https://user-images.githubusercontent.com/737188/226393677-6ce419be-1307-4db6-8e94-f0ba338e6b75.mov


You can see the _actual_ current index and caption below the framer animation. The moment I click faster than the animation takes, framer gets out of sync, despite a unique key being applied per the docs.

I don’t think this is fixed on 10.16.5.

If a page change with router is involved, and the page is changed very quickly back and forth, the page and url desync which causes a very weird bug. The entire page bugs out making it render the wrong component router element for a url route.

I don’t know if this is a different issue though. I’ve had it for a while and 10.16.5 did not fix it.

I’m unable to make a sandbox example at this moment, maybe someone else who’s having this issue could confirm.

thanks @mattgperry 10.16.5 works for me too 🎉

@mattgperry Thanks so much for merging the PR! In 10.16.5 the issue is solved in my test case:

https://codesandbox.io/s/framer-motion-animatepresence-bug-w4ycgz

I can spot some issue as well with mode="popLayout"

Having this example:

const contentVariants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
};


  <AnimatePresence mode="popLayout">
          {tab === TABS.SEND ? (
            <motion.div
              className="w-full"
              key="send"
              initial="hidden"
              animate="visible"
              exit="hidden"
              variants={contentVariants}
              transition={{ duration: 0.5 }}
            >
              <Send />
            </motion.div>
          ) : (
            <motion.div
              className="w-full"
              key="receive"
              initial="hidden"
              animate="visible"
              exit="hidden"
              variants={contentVariants}
              transition={{ duration: 0.5 }}
            >
              <Receive />
            </motion.div>
          )}
        </AnimatePresence>

when switching between tabs kinda fast, content might get mixed and lets say i’m on Send tab, i can see Send and Receive content under the same tab any ideas

My latest patch have finally fixed this issue. Here you can test it

The exit transition lifecycle seems to be still problematic though. I will have a look at it as well.

It does seem to be exclusively a problem with mode="wait" if that helps! 🤘

@mattgperry I think this is related to some tests in a PR you recently merged (#2119)…

I’m still seeing this issue persist even on the latest release (10.12.11).

Rapid changes cause items to not enter/exit properly on subsequent changes.

This is causing a real fuss in production, especially when you start doing scroll-related animations:

  1. Previously removed motion components are not returning after rapid exits from AnimatePresence
  2. Rapid key changes cause the current motion component to “stick” for subsequent changes, and then disappear for others

It’s difficult to pinpoint what framer-motion is doing behind the scenes, but I can 100% confirm that the key is unique and properly updated regardless of how fast the change happens, but it seems that framer motion can’t keep up or resync itself once the dust has settled.

Fixed for me!

Literally just ran into this issue for like the fifth time in two years. Sick of workarounds, was considering going back to RTG for enter/exit.

Checked this thread on a whim and woah! Great work, no more breaking for me on 10.16.9

10.12.22 have this problem too

Still having this issue on 10.16.4. Any workarounds?

My patch should fix this bug. Your feedback is welcome

thanks for the attention guys 🥰

@hatsumatsu the minimal repro sandbox is great - very concise and clearly showcases the issue

@mattgperry let’s do this!! 💪

The problem might be in this method.

Probably one of these early returns should be patched. I am at moment not able to work on it but hope that it might help anyone else debugging the issue.

this bug is driving me crazy and comes up randomly in a not-always-reproducible fashion in production code where timing is a factor of client browser, backend job duration, react component states updating…

My current solution is to just comment out all exit props from variants and motion elements, which is obviously not ideal and ruins 50% of the reason we use framer-motion in the first place. It’s making me seriously consider migrating away from this library completely, as animating a component while exiting and letting an entering component (often times the final value of some task that you want to display to the user) take its place is a huge deal, and can not break in a random fashion in production code! 😦

Same problem here. Last version working for me is 10.5.0. 10.6.0 breaks.

I can confirm this still happens in the latest version. We have an animated modal dialog based on <AnimatePresence> which gets permanently stuck if the user closes it too soon after opening. Rolling back to 9.0.2 fixes the issue.

Downgrading to 9.0.2 fixed the issue for me. Thanks @benjaminwaterlot!

I also have this maddening problem! popLayout is not a viable solution as it seems to actually pop the component outside of its normal layout, thereby not obeying a parent’s overflow:hidden setting

I’m having the same issue switching between two components based on a state update: reproduction

Works fine on ^9.0

This is also happening for me as well after upgrading from 10.3.4 to 10.4.0. Looks like it was introduced as a regression in 10.4.0. It’s especially easy to reproduce in unit tests where click events happen very quickly.