Skip to content

[BUG] animate change made while inside a hidden <Activity> is ignored on reveal #3942

Open

Description

@mdluo

1. Read the FAQs

Done.

2. Describe the bug

A motion.div that first mounts inside <Activity mode="hidden"> ignores any animate change made before the Activity is shown. On reveal it animates to the animate value from its first render.

In useVisualElement, the insertion effect only calls visualElement.update(props, presenceContext) once isMounted.current is true, and isMounted is set in a layout effect. A hidden Activity runs no layout effects, so every re-render before the reveal skips update. When the Activity is shown, animateChanges() runs against the props from the first render.

This is close to #3832 but a different case: there the element had mounted visibly before being hidden. Here it has never been visible.

3. Reproduction

https://stackblitz.com/github/mdluo/motion-activity-repro (run npm install && npm test; source: https://github.com/mdluo/motion-activity-repro). The first test fails, the second is a control that passes.

import { render, waitFor } from "@testing-library/react"
import { motion } from "framer-motion"
import { Activity } from "react"

function Panel({ shown, active }: { shown: boolean; active: boolean }) {
    return (
        <Activity mode={shown ? "visible" : "hidden"}>
            <motion.div
                data-testid="box"
                initial={{ opacity: 0 }}
                animate={{ opacity: active ? 1 : 0 }}
            />
        </Activity>
    )
}

test("animate prop changed while hidden is used on reveal", async () => {
    const { rerender, getByTestId } = render(<Panel shown={false} active={false} />)
    rerender(<Panel shown={false} active />)
    rerender(<Panel shown active />)
    await waitFor(() => expect(getByTestId("box").style.opacity).toBe("1"))
})

4. Steps to reproduce

  1. Mount the element inside a hidden <Activity> with animate={{ opacity: 0 }}.
  2. While still hidden, re-render with animate={{ opacity: 1 }}.
  3. Show the Activity.
  4. Opacity stays at 0.

5. Expected behavior

On reveal the element animates to opacity: 1, the animate value of the latest render.

6. Environment details

framer-motion 13.4.4 and 14.3.0, React 19.2.0 and 19.3.0. Seen in Chrome; the test fails in jsdom and happy-dom.

Possible fix

This passes the test above and our app's suites. We are running it as a local patch:

// useVisualElement, in the layout effect that sets isMounted
if (!isMounted.current) visualElement.update(props, presenceContext)
isMounted.current = true

The repro and the patch were written with an AI assistant; we ran the test against both the stock and the patched package.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions