Skip to content

ReanimatedSwipeable: Two animation bugs - actions disappearing and instant snap on fast swipe #3897

@ugoi

Description

@ugoi

Description

I discovered two animation bugs in ReanimatedSwipeable that cause poor UX during swipe interactions.

Bug 1: Right/Left actions disappear during fast close animation

When closing a swipeable quickly, the action views (left/right) disappear before the animation completes, leaving only the background visible.

Root Cause: The opacity of action containers is set conditionally:

opacity: showRightProgress.value === 0 ? 0 : 1

When the progress value hits 0 (even momentarily during animation), the actions become invisible while the row is still animating back.

Fix: Set opacity to always be 1:

opacity: 1

The actions are already clipped by overflow: 'hidden' on the container, so they don't need opacity-based hiding.


Bug 2: Fast swipe causes instant snap without animation

When swiping quickly, the row snaps instantly without any visible animation. This happens in two scenarios:

  1. Closing from open: Swipe fast to close → snaps instantly
  2. Staying open: Swipe open, then swipe back fast (but not enough to close) → snaps back to open instantly

Slow swipes animate smoothly in both cases.

Root Cause: When you swipe fast, the velocity opposes the spring's target direction:

  • Example (closing): currentPos = -20, velocity = -300 (moving left), target = 0 (right)
  • Example (staying open): currentPos = -80, velocity = +300 (moving right), target = -100 (left)

With overshootClamping: true and high stiffness, the spring fights against the velocity and completes in just a few frames, appearing instant.

Fix: Ignore velocity when it opposes the direction towards the target:

const currentPos = appliedTranslation.value;
const movingTowardsTarget = clampedVelocity
  ? (toValue > currentPos && clampedVelocity > 0) || (toValue < currentPos && clampedVelocity < 0)
  : true;
const effectiveVelocity = !movingTowardsTarget ? 0 : clampedVelocity;

This allows the spring to animate naturally without fighting the swipe momentum.


Steps to reproduce

Bug 1 (Actions disappearing):

  1. Create a ReanimatedSwipeable with renderRightActions
  2. Swipe left to reveal the right action
  3. Quickly swipe back to close
  4. Observe: The right action disappears mid-animation

Bug 2 (Instant snap - closing):

  1. Create a ReanimatedSwipeable with renderRightActions
  2. Swipe left very fast (quick flick) and release immediately
  3. Observe: The row snaps back instantly without animation

Bug 2 (Instant snap - staying open):

  1. Swipe left to fully open the swipeable (reveal right action)
  2. Swipe right very fast but release before it closes
  3. Observe: The row snaps back to open position instantly without animation

Compare all above with slow swipes - they animate smoothly.

Snack or a link to a repository

https://snack.expo.dev/@ugoi/reanimatedswipeable-animation-bugs

⚠️ Note: Run on Web tab - the Snack iOS/Android simulator has an unrelated gesture-handler registration bug.

Gesture Handler version

2.28.0

React Native version

0.81.5

Platforms

platform affected
iOS ✅ Yes
Android ✅ Yes
Web ✅ Yes

Proposed Solution

I have working patches for both issues. Happy to submit a PR.

For Bug 1: Change lines 359 and 389 in ReanimatedSwipeable.tsx:

// Before
opacity: showRightProgress.value === 0 ? 0 : 1

// After
opacity: 1

For Bug 2: Add velocity direction detection in animateRow function (~line 198):

const MAX_VELOCITY = 500;
const clampedVelocity = velocityX
  ? Math.max(-MAX_VELOCITY, Math.min(MAX_VELOCITY, velocityX))
  : undefined;

const currentPos = appliedTranslation.value;
const movingTowardsTarget = clampedVelocity
  ? (toValue > currentPos && clampedVelocity > 0) || (toValue < currentPos && clampedVelocity < 0)
  : true;
const effectiveVelocity = !movingTowardsTarget ? 0 : clampedVelocity;

const translationSpringConfig = {
  // ... use effectiveVelocity instead of velocityX
};

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions