Skip to main content
The ElasticCard preset creates a screen that can be dragged with elastic resistance in any direction, featuring a dynamic backdrop overlay that darkens the previous screen.

Function Signature

ElasticCard(
  config?: Partial<ScreenTransitionConfig> & {
    elasticFactor?: number;
  }
): ScreenTransitionConfig

Parameters

config
object
default:"{ elasticFactor: 0.5 }"
Configuration object with optional elasticFactor

Returns

ScreenTransitionConfig
object
Configuration object with bidirectional gesture and backdrop settings

Implementation Details

Screen Style Interpolator

The preset combines scale, elastic translation, and backdrop overlay:
screenStyleInterpolator: ({
  current,
  next,
  layouts: { screen },
  progress,
}) => {
  "worklet";

  // Applies to both screens (previous and incoming)
  const scale = interpolate(progress, [0, 1, 2], [0, 1, 0.8]);

  // Applies to current screen - elastic drag effect
  const maxElasticityX = screen.width * (config.elasticFactor ?? 0.5);
  const maxElasticityY = screen.height * (config.elasticFactor ?? 0.5);
  const translateX = interpolate(
    current.gesture.normalizedX,
    [-1, 0, 1],
    [-maxElasticityX, 0, maxElasticityX],
    "clamp",
  );
  const translateY = interpolate(
    current.gesture.normalizedY,
    [-1, 0, 1],
    [-maxElasticityY, 0, maxElasticityY],
    "clamp",
  );

  // Applies to unfocused screen (previous screen) - backdrop overlay
  const overlayColor = interpolateColor(
    progress,
    [0, 1],
    ["rgba(0,0,0,0)", "rgba(0,0,0,0.5)"],
  );

  return {
    contentStyle: {
      transform: [{ scale }, { translateX }, { translateY }],
    },
    backdropStyle: {
      backgroundColor: !next ? overlayColor : "rgba(0,0,0,0)",
    },
  };
}
Animation behavior:
  • progress = 0: Screen is scaled to 0 (invisible)
  • progress = 1: Screen is at normal size with backdrop at 50% opacity
  • progress = 2: Screen scales to 80% as it exits
  • Elastic drag distance is configurable via elasticFactor
  • Backdrop only appears when there’s no next screen (top of stack)

Transition Spec

transitionSpec: {
  open: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restSpeedThreshold: 0.02,
  },
  close: {
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restSpeedThreshold: 0.02,
  },
}

Usage Examples

import Transition from "react-native-screen-transitions";

<Stack.Screen
  name="Card"
  options={{
    ...Transition.Presets.ElasticCard(),
  }}
/>

Common Use Cases

  • iOS-style modals - Elastic feel similar to native iOS sheets
  • Preview overlays - Quick-look screens with natural drag behavior
  • Settings panels - Configuration screens with satisfying feedback
  • Detail views - Product or profile screens with playful interaction
  • Photo viewers - Image screens with natural drag-to-dismiss

Elastic Behavior Explained

The elastic effect means the screen resists being dragged but still follows the gesture:
// With elasticFactor: 0.5
// If screen width is 400px:
// - Maximum drag distance = 400 * 0.5 = 200px
// - Gesture normalizedX of 1.0 = 200px translation
// - Gesture normalizedX of 0.5 = 100px translation

// With elasticFactor: 0.3 (more resistance)
// - Maximum drag distance = 400 * 0.3 = 120px
// - Same gesture feels "stiffer"

Backdrop Behavior

The backdrop darkens the previous screen as the new screen appears:
  • Only visible when screen is on top of stack (!next condition)
  • Fades from transparent to 50% black overlay
  • Creates depth perception in the stack
  • Can be customized with different colors/opacity

Notes

The elasticFactor only affects gesture-driven drag, not the enter/exit animation. The scale animation is always tied to the progress value.
For a native iOS feel, use elasticFactor: 0.4 to 0.5. For a more playful, exaggerated effect, use 0.6 to 0.8.
The bidirectional gesture may conflict with horizontal scrolling. Use Transition.ScrollView for proper gesture coordination with scrollable content.