Skip to content

Add settled prop for declarative animation suppression#3585

Closed
mattgperry wants to merge 1 commit intomainfrom
worktree-fix-issue-3560
Closed

Add settled prop for declarative animation suppression#3585
mattgperry wants to merge 1 commit intomainfrom
worktree-fix-issue-3560

Conversation

@mattgperry
Copy link
Collaborator

Summary

  • Adds a settled boolean prop to motion components that suppresses animation interpolation when false
  • When settled={false}, all animations apply instantly (type: false), equivalent to CSS transition: none
  • When settled transitions from false to true, the current animate value applies instantly (no catch-up animation), and only subsequent changes respect the configured transition

Problem

When async measurement systems (e.g. IntersectionObserver, ResizeObserver) fire multiple corrections during initialization, each state change triggers a new animation in motion components. There was no declarative way to suppress interpolation during this unstable period. The existing APIs (initial={false}, transition: { duration: 0 }, transition.delay) each fail to fully address the measurement flutter problem as detailed in #3560.

Implementation

The fix is minimal — two changes in motion-dom:

  1. Type definition (node/types.ts): Added settled?: boolean to MotionNodeAnimationOptions
  2. Animation behavior (render/utils/animation-state.ts): In animateChanges(), when the component is unsettled (settled === false or settled just flipped from false to true), all animations receive transitionOverride: { type: false } which applies values instantly with no interpolation

Usage

<motion.div
  animate={{ opacity: isVisible ? 1 : 0 }}
  transition={{ duration: 0.3 }}
  settled={isSettled}
/>

Test plan

  • Unit test: values applied instantly when settled={false}
  • Unit test: rapid changes applied instantly when settled={false}
  • Unit test: normal animation resumes after settled becomes true
  • Unit test: settled false→true transition applies instantly (no catch-up)
  • All existing animate-prop tests pass (no regressions)
  • Full test suite passes

Fixes #3560

🤖 Generated with Claude Code

When settled={false}, all animations are applied instantly (type: false)
with no interpolation, equivalent to CSS transition: none. When settled
transitions from false to true, the current animate value is applied
instantly to prevent catch-up animations. Only subsequent changes after
settled is true use the configured transition.

Fixes #3560

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link

greptile-apps bot commented Mar 3, 2026

Greptile Summary

Added settled boolean prop to declaratively suppress animation interpolation during measurement instability. When settled={false}, all animations apply instantly (equivalent to transition: none). When transitioning from false to true, the current value applies instantly without catch-up animation, and subsequent changes resume normal animation behavior. Implementation is minimal and well-tested with comprehensive unit tests covering all state transitions.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Implementation is minimal (10 lines of core logic), well-documented, and comprehensively tested with 4 unit tests covering all state transitions. Logic correctly handles edge cases including false→true transitions, rapid changes while unsettled, and resumption of normal animation behavior. No breaking changes or side effects detected.
  • No files require special attention

Important Files Changed

Filename Overview
packages/motion-dom/src/node/types.ts Added settled?: boolean prop to type definition with clear documentation
packages/motion-dom/src/render/utils/animation-state.ts Implemented animation suppression logic that correctly handles settled state transitions
packages/framer-motion/src/motion/tests/animate-prop.test.tsx Added comprehensive test coverage for all settled prop behaviors and edge cases

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[animateChanges called] --> B{props.settled === false?}
    B -->|Yes| C[isUnsettled = true]
    B -->|No| D{prevProps.settled === false?}
    D -->|Yes| C
    D -->|No| E[isUnsettled = false]
    C --> F[Apply transitionOverride: type false]
    F --> G[Animation applies instantly]
    E --> H[Normal transition behavior]
    H --> I[Animation interpolates over time]
Loading

Last reviewed commit: 36dd6b3

@mattgperry mattgperry closed this Mar 4, 2026
@mattgperry mattgperry deleted the worktree-fix-issue-3560 branch March 4, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Declarative animation suppression during measurement initialization

1 participant