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.

Build docs developers (and LLMs) love