{"id":37426723,"url":"https://github.com/eds2002/react-native-screen-transitions","last_synced_at":"2026-04-02T18:51:42.362Z","repository":{"id":304665931,"uuid":"1018237763","full_name":"eds2002/react-native-screen-transitions","owner":"eds2002","description":"Easy screen transitions 😎","archived":false,"fork":false,"pushed_at":"2026-03-30T04:48:14.000Z","size":163517,"stargazers_count":1310,"open_issues_count":1,"forks_count":35,"subscribers_count":7,"default_branch":"main","last_synced_at":"2026-03-30T06:57:03.010Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eds2002.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-11T21:17:21.000Z","updated_at":"2026-03-30T06:42:30.000Z","dependencies_parsed_at":"2025-07-14T16:50:17.225Z","dependency_job_id":"0fcdc7e7-0f9f-4d4c-9f56-6f5dc5cb7389","html_url":"https://github.com/eds2002/react-native-screen-transitions","commit_stats":null,"previous_names":["eds2002/react-native-screen-transitions"],"tags_count":71,"template":false,"template_full_name":null,"purl":"pkg:github/eds2002/react-native-screen-transitions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eds2002%2Freact-native-screen-transitions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eds2002%2Freact-native-screen-transitions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eds2002%2Freact-native-screen-transitions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eds2002%2Freact-native-screen-transitions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eds2002","download_url":"https://codeload.github.com/eds2002/react-native-screen-transitions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eds2002%2Freact-native-screen-transitions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31313478,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-16T06:17:26.942Z","updated_at":"2026-04-02T18:51:42.335Z","avatar_url":"https://github.com/eds2002.png","language":"TypeScript","readme":"# react-native-screen-transitions\n\nCustomizable screen transitions for React Native. Build gesture-driven, shared element, and fully custom animations with a simple API.\n\n| iOS                                                                                                                                     | Android                                                                                                                    |\n| --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |\n| \u003cvideo src=\"https://github.com/user-attachments/assets/c0d17b8f-7268-421c-9051-e242f8ddca76\" width=\"300\" height=\"600\" controls\u003e\u003c/video\u003e | \u003cvideo src=\"https://github.com/user-attachments/assets/3f8d5fb1-96d2-4fe3-860d-62f6fb5a687e\" width=\"300\" controls\u003e\u003c/video\u003e |\n\n## Features\n\n- **Full Animation Control** – Define exactly how screens enter, exit, and respond to gestures\n- **Shared Elements** – Smooth transitions between screens using the Bounds API\n- **Gesture Support** – Swipe-to-dismiss with edge or full-screen activation\n- **Stack Progress** – Track animation progress across the entire stack\n- **Ready-Made Presets** – Instagram, Apple Music, X (Twitter) style transitions included\n\n## When to Use This Library\n\n| Use Case | This Library | Alternative |\n|----------|--------------|-------------|\n| Custom transitions (slide, zoom, fade variations) | Yes | `@react-navigation/stack` works too |\n| Shared element transitions | **Yes** | Limited options elsewhere |\n| Multi-stop sheets (bottom, top, side) with snap points | **Yes** | Dedicated sheet libraries |\n| Gesture-driven animations (drag to dismiss, elastic) | **Yes** | Requires custom implementation |\n| Instagram/Apple Music/Twitter-style transitions | **Yes** | Custom implementation |\n| Simple push/pop with platform defaults | Overkill | `@react-navigation/native-stack` |\n| Maximum raw performance on low-end devices | Not ideal | `@react-navigation/native-stack` |\n\n**Choose this library when** you need custom animations, shared elements, or gesture-driven transitions that go beyond platform defaults.\n\n**Choose native-stack when** you want platform-native transitions with zero configuration and maximum performance on low-end Android devices.\n\n## Installation\n\n```bash\nnpm install react-native-screen-transitions\n```\n\n### Peer Dependencies\n\n```bash\nnpm install react-native-reanimated react-native-gesture-handler \\\n  @react-navigation/native @react-navigation/native-stack \\\n  @react-navigation/elements react-native-screens \\\n  react-native-safe-area-context\n```\n\n---\n\n## Quick Start\n\n### 1. Create a Stack\n\n```tsx\nimport { createBlankStackNavigator } from \"react-native-screen-transitions/blank-stack\";\nimport Transition from \"react-native-screen-transitions\";\n\nconst Stack = createBlankStackNavigator();\n\nfunction App() {\n  return (\n    \u003cStack.Navigator\u003e\n      \u003cStack.Screen name=\"Home\" component={HomeScreen} /\u003e\n      \u003cStack.Screen\n        name=\"Detail\"\n        component={DetailScreen}\n        options={{\n          ...Transition.Presets.SlideFromBottom(),\n        }}\n      /\u003e\n    \u003c/Stack.Navigator\u003e\n  );\n}\n```\n\n### 2. With Expo Router\n\n```tsx\nimport { withLayoutContext } from \"expo-router\";\nimport {\n  createBlankStackNavigator,\n  type BlankStackNavigationOptions,\n} from \"react-native-screen-transitions/blank-stack\";\n\nconst { Navigator } = createBlankStackNavigator();\n\nexport const Stack = withLayoutContext\u003c\n  BlankStackNavigationOptions,\n  typeof Navigator\n\u003e(Navigator);\n```\n\n---\n\n## Presets\n\nUse built-in presets for common transitions:\n\n```tsx\n\u003cStack.Screen\n  name=\"Detail\"\n  options={{\n    ...Transition.Presets.SlideFromBottom(),\n  }}\n/\u003e\n```\n\n| Preset                                 | Description                             |\n| -------------------------------------- | --------------------------------------- |\n| `SlideFromTop()`                       | Slides in from top                      |\n| `SlideFromBottom()`                    | Slides in from bottom (modal-style)     |\n| `ZoomIn()`                             | Scales in with fade                     |\n| `DraggableCard()`                      | Multi-directional drag with scaling     |\n| `ElasticCard()`                        | Elastic drag with overlay               |\n| `SharedIGImage({ sharedBoundTag })`    | Instagram-style shared image            |\n| `SharedAppleMusic({ sharedBoundTag })` | Apple Music-style shared element        |\n| `SharedXImage({ sharedBoundTag })`     | X (Twitter)-style image transition      |\n\n---\n\n## Custom Animations\n\n### The Basics\n\nEvery screen has a `progress` value that goes from 0 → 1 → 2:\n\n```\n0 ─────────── 1 ─────────── 2\nentering     visible      exiting\n```\n\nWhen navigating from A to B:\n- **Screen B**: progress goes `0 → 1` (entering)\n- **Screen A**: progress goes `1 → 2` (exiting)\n\n### Simple Fade\n\n```tsx\noptions={{\n  screenStyleInterpolator: ({ progress }) =\u003e {\n    \"worklet\";\n    return {\n      contentStyle: {\n        opacity: interpolate(progress, [0, 1, 2], [0, 1, 0]),\n      },\n    };\n  },\n}}\n```\n\n### Slide from Right\n\n```tsx\noptions={{\n  screenStyleInterpolator: ({ progress, layouts: { screen } }) =\u003e {\n    \"worklet\";\n    return {\n      contentStyle: {\n        transform: [{\n          translateX: interpolate(\n            progress,\n            [0, 1, 2],\n            [screen.width, 0, -screen.width * 0.3]\n          ),\n        }],\n      },\n    };\n  },\n}}\n```\n\n### Slide from Bottom\n\n```tsx\noptions={{\n  screenStyleInterpolator: ({ progress, layouts: { screen } }) =\u003e {\n    \"worklet\";\n    return {\n      contentStyle: {\n        transform: [{\n          translateY: interpolate(progress, [0, 1], [screen.height, 0]),\n        }],\n      },\n    };\n  },\n}}\n```\n\n### Return Styles\n\nYour interpolator can return:\n\n```tsx\nreturn {\n  contentStyle: { ... },   // Main screen\n  backdropStyle: { ... },  // Semi-transparent backdrop\n  [\"my-id\"]: { ... },      // Specific element via styleId\n};\n```\n\n### Animation Specs\n\nControl timing with spring configs:\n\n```tsx\noptions={{\n  screenStyleInterpolator: myInterpolator,\n  transitionSpec: {\n    open: { stiffness: 1000, damping: 500, mass: 3 },    // Screen enters\n    close: { stiffness: 1000, damping: 500, mass: 3 },   // Screen exits\n    expand: { stiffness: 300, damping: 30 },             // Snap point increases\n    collapse: { stiffness: 300, damping: 30 },           // Snap point decreases\n  },\n}}\n```\n\n---\n\n## Gestures\n\nEnable swipe-to-dismiss:\n\n```tsx\noptions={{\n  gestureEnabled: true,\n  gestureDirection: \"vertical\",\n  ...Transition.Presets.SlideFromBottom(),\n}}\n```\n\n### Gesture Options\n\n| Option                    | Description                                                              |\n| ------------------------- | ------------------------------------------------------------------------ |\n| `gestureEnabled`          | Enable swipe-to-dismiss (snap sheets: `false` blocks dismiss-to-0 only) |\n| `gestureDirection`        | Direction(s) for swipe gesture                                           |\n| `gestureActivationArea`   | Where gesture can start                                                  |\n| `gestureResponseDistance` | Pixel threshold for activation                                           |\n| `gestureVelocityImpact`   | How much velocity affects dismissal (default: 0.3)                       |\n| `gestureDrivesProgress`   | Whether gesture controls animation progress (default: true)              |\n| `snapVelocityImpact`      | How much velocity affects snap targeting (default: 0.1, lower = iOS-like)|\n| `expandViaScrollView`     | Allow expansion from ScrollView at boundary (default: true)               |\n| `gestureSnapLocked`       | Lock gesture-based snap movement to current snap point                   |\n| `backdropBehavior`        | Touch handling for backdrop area                                         |\n| `backdropComponent`       | Custom backdrop component (replaces default backdrop + press behavior)   |\n\n### Gesture Direction\n\n```tsx\ngestureDirection: \"horizontal\"          // swipe left to dismiss\ngestureDirection: \"horizontal-inverted\" // swipe right to dismiss\ngestureDirection: \"vertical\"            // swipe down to dismiss\ngestureDirection: \"vertical-inverted\"   // swipe up to dismiss\ngestureDirection: \"bidirectional\"       // any direction\n\n// Or combine multiple:\ngestureDirection: [\"horizontal\", \"vertical\"]\n```\n\n### Gesture Activation Area\n\n```tsx\n// Simple - same for all edges\ngestureActivationArea: \"edge\"    // only from screen edges\ngestureActivationArea: \"screen\"  // anywhere on screen\n\n// Per-side configuration\ngestureActivationArea: {\n  left: \"edge\",\n  right: \"screen\",\n  top: \"edge\",\n  bottom: \"screen\",\n}\n```\n\n### With ScrollViews\n\nUse transition-aware scrollables so gestures work correctly:\n\n```tsx\n\u003cTransition.ScrollView\u003e\n  {/* content */}\n\u003c/Transition.ScrollView\u003e\n\n\u003cTransition.FlatList data={items} renderItem={...} /\u003e\n```\n\nGesture rules with scrollables:\n- **vertical** – only activates when scrolled to top\n- **vertical-inverted** – only activates when scrolled to bottom\n- **horizontal** – only activates at left/right scroll edges\n\n---\n\n## Snap Points\n\nCreate multi-stop sheets that snap to defined positions. Works with any gesture direction (bottom sheets, top sheets, side sheets):\n\n### Basic Configuration\n\n```tsx\n// Bottom sheet (most common)\n\u003cStack.Screen\n  name=\"Sheet\"\n  options={{\n    gestureEnabled: true,\n    gestureDirection: \"vertical\",\n    snapPoints: [0.5, 1],         // 50% and 100% of screen\n    initialSnapIndex: 0,          // Start at 50%\n    backdropBehavior: \"dismiss\",  // Tap backdrop to dismiss\n    ...Transition.Presets.SlideFromBottom(),\n  }}\n/\u003e\n\n// Side sheet (same API, different direction)\n\u003cStack.Screen\n  name=\"SidePanel\"\n  options={{\n    gestureEnabled: true,\n    gestureDirection: \"horizontal\",\n    snapPoints: [0.3, 0.7, 1],    // 30%, 70%, 100% of screen width\n    initialSnapIndex: 1,\n    // Add a horizontal screenStyleInterpolator for drawer-style motion\n  }}\n/\u003e\n```\n\n### Options\n\n| Option             | Description                                                          |\n| ------------------ | -------------------------------------------------------------------- |\n| `snapPoints`       | Array of fractions (0-1) where sheet can rest                        |\n| `initialSnapIndex` | Index of initial snap point (default: 0)                             |\n| `gestureSnapLocked` | Locks gesture snapping to current point (programmatic `snapTo` still works) |\n| `backdropBehavior` | Touch handling: `\"block\"`, `\"passthrough\"`, `\"dismiss\"`, `\"collapse\"`|\n| `backdropComponent` | Custom backdrop component; replaces default backdrop + tap handling    |\n\n#### backdropBehavior Values\n\n| Value           | Description                                                      |\n| --------------- | ---------------------------------------------------------------- |\n| `\"block\"`       | Backdrop catches all touches (default)                           |\n| `\"passthrough\"` | Touches pass through to content behind                           |\n| `\"dismiss\"`     | Tapping backdrop dismisses the screen                            |\n| `\"collapse\"`    | Tapping backdrop collapses to next lower snap point, then dismisses |\n\n#### Custom Backdrop Component\n\nUse `backdropComponent` when you want full control over backdrop visuals and interactions.\n\n- When provided, it replaces the default backdrop entirely (including default tap behavior)\n- You are responsible for dismiss/collapse actions inside the custom component\n- `backdropBehavior` still controls container-level pointer event behavior\n\n```tsx\nimport { router } from \"expo-router\";\nimport { Pressable } from \"react-native\";\nimport Animated, { interpolate, useAnimatedStyle } from \"react-native-reanimated\";\nimport { useScreenAnimation } from \"react-native-screen-transitions\";\n\nfunction SheetBackdrop() {\n  const animation = useScreenAnimation();\n\n  const style = useAnimatedStyle(() =\u003e ({\n    opacity: interpolate(animation.value.current.progress, [0, 1], [0, 0.4]),\n    backgroundColor: \"#000\",\n  }));\n\n  return (\n    \u003cPressable style={{ flex: 1 }} onPress={() =\u003e router.back()}\u003e\n      \u003cAnimated.View style={[{ flex: 1 }, style]} /\u003e\n    \u003c/Pressable\u003e\n  );\n}\n\n\u003cStack.Screen\n  name=\"Sheet\"\n  options={{\n    snapPoints: [0.5, 1],\n    backdropBehavior: \"dismiss\",\n    backdropComponent: SheetBackdrop,\n  }}\n/\u003e\n```\n\n### Programmatic Control\n\nControl snap points from anywhere in your app:\n\n```tsx\nimport { snapTo } from \"react-native-screen-transitions\";\n\nfunction BottomSheet() {\n  // Expand to full height (index 1)\n  const expand = () =\u003e snapTo(1);\n\n  // Collapse to half height (index 0)\n  const collapse = () =\u003e snapTo(0);\n\n  return (\n    \u003cView\u003e\n      \u003cButton title=\"Expand\" onPress={expand} /\u003e\n      \u003cButton title=\"Collapse\" onPress={collapse} /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\nThe animated `snapIndex` is available in screen interpolators via `ScreenInterpolationProps`:\n\n```tsx\nscreenStyleInterpolator: ({ snapIndex }) =\u003e {\n  // snapIndex interpolates between snap point indices\n  // e.g., 0.5 means halfway between snap point 0 and 1\n  return {\n    contentStyle: {\n      opacity: interpolate(snapIndex, [0, 1], [0.5, 1]),\n    },\n  };\n}\n```\n\n### ScrollView Behavior\n\nWith `Transition.ScrollView` inside a snap-enabled sheet:\n- **`expandViaScrollView: true`**: At boundary, swipe up expands and swipe down collapses (or dismisses at min if enabled)\n- **`expandViaScrollView: false`**: Expand works only via deadspace; collapse/dismiss via scroll still works at boundary\n- **Scrolled into content**: Normal scroll behavior\n\n### Snap Animation Specs\n\nCustomize snap animations separately from enter/exit:\n\n```tsx\ntransitionSpec: {\n  open: { stiffness: 1000, damping: 500, mass: 3 },   // Screen enter\n  close: { stiffness: 1000, damping: 500, mass: 3 },  // Screen exit\n  expand: { stiffness: 300, damping: 30 },            // Snap up\n  collapse: { stiffness: 300, damping: 30 },          // Snap down\n}\n```\n\n---\n\n## Shared Elements (Bounds API)\n\nAnimate elements between screens by tagging them.\n\n### 1. Tag the Source\n\n```tsx\n\u003cTransition.Pressable\n  sharedBoundTag=\"avatar\"\n  onPress={() =\u003e navigation.navigate(\"Profile\")}\n\u003e\n  \u003cImage source={avatar} style={{ width: 50, height: 50 }} /\u003e\n\u003c/Transition.Pressable\u003e\n```\n\n### 2. Tag the Destination\n\n```tsx\n\u003cTransition.View sharedBoundTag=\"avatar\"\u003e\n  \u003cImage source={avatar} style={{ width: 200, height: 200 }} /\u003e\n\u003c/Transition.View\u003e\n```\n\n### 3. Use in Interpolator\n\n```tsx\nscreenStyleInterpolator: ({ bounds }) =\u003e {\n  \"worklet\";\n  return {\n    avatar: bounds({ id: \"avatar\", method: \"transform\" }),\n  };\n};\n```\n\n### Bounds Options\n\n| Option      | Values                             | Description                   |\n| ----------- | ---------------------------------- | ----------------------------- |\n| `id`        | string                             | The `sharedBoundTag` to match |\n| `method`    | `\"transform\"` `\"size\"` `\"content\"` | How to animate                |\n| `space`     | `\"relative\"` `\"absolute\"`          | Coordinate space              |\n| `scaleMode` | `\"match\"` `\"none\"` `\"uniform\"`     | Aspect ratio handling         |\n| `raw`       | boolean                            | Return raw values             |\n\n---\n\n## Overlays\n\nPersistent UI that animates with the stack:\n\n```tsx\nconst TabBar = ({ focusedIndex, progress }) =\u003e {\n  const style = useAnimatedStyle(() =\u003e ({\n    transform: [{ translateY: interpolate(progress.value, [0, 1], [100, 0]) }],\n  }));\n  return \u003cAnimated.View style={[styles.tabBar, style]} /\u003e;\n};\n\n\u003cStack.Screen\n  name=\"Home\"\n  options={{\n    overlay: TabBar,\n    overlayShown: true,\n  }}\n/\u003e\n```\n\n### Overlay Props\n\n| Prop           | Description                    |\n| -------------- | ------------------------------ |\n| `focusedRoute` | Currently focused route        |\n| `focusedIndex` | Index of focused screen        |\n| `routes`       | All routes in the stack        |\n| `progress`     | Stack progress (derived value) |\n| `navigation`   | Navigation prop                |\n| `meta`         | Custom metadata from options   |\n\n---\n\n## Transition Components\n\n| Component               | Description                            |\n| ----------------------- | -------------------------------------- |\n| `Transition.View`       | Animated view with `sharedBoundTag`    |\n| `Transition.Pressable`  | Pressable that measures bounds         |\n| `Transition.ScrollView` | ScrollView with gesture coordination   |\n| `Transition.FlatList`   | FlatList with gesture coordination     |\n| `Transition.MaskedView` | For reveal effects (requires native)   |\n\n---\n\n## Hooks\n\n### useScreenAnimation\n\nAccess animation state inside a screen:\n\n```tsx\nimport { useScreenAnimation } from \"react-native-screen-transitions\";\n\nfunction DetailScreen() {\n  const animation = useScreenAnimation();\n\n  const style = useAnimatedStyle(() =\u003e ({\n    opacity: animation.value.current.progress,\n  }));\n\n  return \u003cAnimated.View style={style}\u003e...\u003c/Animated.View\u003e;\n}\n```\n\n### useScreenState\n\nGet navigation state without animation values:\n\n```tsx\nimport { useScreenState } from \"react-native-screen-transitions\";\n\nfunction DetailScreen() {\n  const { index, focusedRoute, routes, navigation } = useScreenState();\n  // ...\n}\n```\n\n### useHistory\n\nAccess navigation history across the app:\n\n```tsx\nimport { useHistory } from \"react-native-screen-transitions\";\n\nfunction MyComponent() {\n  const { getRecent, getPath } = useHistory();\n\n  const recentScreens = getRecent(5);  // Last 5 screens\n  const path = getPath(fromKey, toKey); // Path between screens\n}\n```\n\n### useScreenGesture\n\nCoordinate your own pan gestures with the navigation gesture:\n\n```tsx\nimport { useScreenGesture } from \"react-native-screen-transitions\";\nimport { Gesture, GestureDetector } from \"react-native-gesture-handler\";\n\nfunction MyScreen() {\n  const screenGesture = useScreenGesture();\n\n  const myPanGesture = Gesture.Pan()\n    .simultaneousWithExternalGesture(screenGesture)\n    .onUpdate((e) =\u003e {\n      // Your gesture logic\n    });\n\n  return (\n    \u003cGestureDetector gesture={myPanGesture}\u003e\n      \u003cView /\u003e\n    \u003c/GestureDetector\u003e\n  );\n}\n```\n\nUse this when you have custom pan gestures that need to work alongside screen dismiss gestures.\n\n---\n\n## Advanced Animation Props\n\nThe full `screenStyleInterpolator` receives these props:\n\n| Prop             | Description                                              |\n| ---------------- | -------------------------------------------------------- |\n| `progress`       | Combined progress (0-2)                                  |\n| `stackProgress`  | Accumulated progress across entire stack                 |\n| `snapIndex`      | Animated snap point index (-1 if no snap points)         |\n| `focused`        | Whether this screen is the topmost in the stack          |\n| `current`        | Current screen state                                     |\n| `previous`       | Previous screen state                                    |\n| `next`           | Next screen state                                        |\n| `active`         | Screen driving the transition                            |\n| `inactive`       | Screen NOT driving the transition                        |\n| `layouts.screen` | Screen dimensions                                        |\n| `insets`         | Safe area insets                                         |\n| `bounds`         | Shared element bounds function                           |\n\n### Screen State Properties\n\nEach screen state (`current`, `previous`, `next`, `active`, `inactive`) contains:\n\n| Property    | Description                              |\n| ----------- | ---------------------------------------- |\n| `progress`  | Animation progress (0 or 1)              |\n| `closing`   | Whether closing (0 or 1)                 |\n| `entering`  | Whether entering (0 or 1)                |\n| `animating` | Whether animating (0 or 1)               |\n| `gesture`   | Gesture values (x, y, normalized values) |\n| `meta`      | Custom metadata from options             |\n\n### Using `meta` for Conditional Logic\n\nPass custom data between screens:\n\n```tsx\n// Screen A\noptions={{ meta: { hideTabBar: true } }}\n\n// Screen B reads it\nscreenStyleInterpolator: (props) =\u003e {\n  \"worklet\";\n  const hideTabBar = props.inactive?.meta?.hideTabBar;\n  // ...\n};\n```\n\n### Animate Individual Elements\n\nUse `styleId` to target specific elements:\n\n```tsx\n// In options\nscreenStyleInterpolator: ({ progress }) =\u003e {\n  \"worklet\";\n  return {\n    \"hero-image\": {\n      opacity: interpolate(progress, [0, 1], [0, 1]),\n    },\n  };\n};\n\n// In component\n\u003cTransition.View styleId=\"hero-image\"\u003e\n  \u003cImage source={...} /\u003e\n\u003c/Transition.View\u003e\n```\n\n---\n\n## Stack Types\n\nAll three stacks share the same animation API. Choose based on your needs:\n\n| Stack               | Best For                                                  |\n| ------------------- | --------------------------------------------------------- |\n| **Blank Stack**     | Most apps. Full control, all features.                    |\n| **Native Stack**    | When you need native screen primitives.                   |\n| **Component Stack** | Embedded flows, isolated from React Navigation. *(Experimental)* |\n\n### Blank Stack\n\nThe default choice. Uses `react-native-screens` for native screen containers, with animations powered by Reanimated worklets running on the UI thread (not the JS thread).\n\n```tsx\nimport { createBlankStackNavigator } from \"react-native-screen-transitions/blank-stack\";\n```\n\n### Native Stack\n\nExtends `@react-navigation/native-stack`. Requires `enableTransitions: true`.\n\n```tsx\nimport { createNativeStackNavigator } from \"react-native-screen-transitions/native-stack\";\n\n\u003cStack.Screen\n  name=\"Detail\"\n  options={{\n    enableTransitions: true,\n    ...Transition.Presets.SlideFromBottom(),\n  }}\n/\u003e\n```\n\n### Component Stack (Experimental)\n\n\u003e **Note:** This API is experimental and may change based on community feedback.\n\nStandalone navigator, not connected to React Navigation. Ideal for embedded flows.\n\n```tsx\nimport { createComponentStackNavigator } from \"react-native-screen-transitions/component-stack\";\n\nconst Stack = createComponentStackNavigator();\n\n\u003cStack.Navigator initialRouteName=\"step1\"\u003e\n  \u003cStack.Screen name=\"step1\" component={Step1} /\u003e\n  \u003cStack.Screen name=\"step2\" component={Step2} /\u003e\n\u003c/Stack.Navigator\u003e\n```\n\n---\n\n## Caveats \u0026 Trade-offs\n\n### Native Stack\n\nThe Native Stack uses transparent modal presentation to intercept transitions. This has trade-offs:\n\n- **Delayed touch events** – Exiting screens may have briefly delayed touch response\n- **beforeRemove listeners** – Relies on navigation lifecycle events\n- **Rapid navigation** – Some edge cases with very fast navigation sequences\n\nFor most apps, Blank Stack avoids these issues entirely.\n\n### Component Stack (Experimental)\n\n- **No deep linking** – Routes aren't part of your URL structure\n- **Isolated state** – Doesn't affect parent navigation\n- **Touch pass-through** – Uses `pointerEvents=\"box-none\"` by default\n\n---\n\n## Experimental Features\n\n### High Refresh Rate\n\nForce maximum refresh rate during transitions (for 90Hz/120Hz displays):\n\n```tsx\noptions={{\n  experimental_enableHighRefreshRate: true,\n}}\n```\n\n---\n\n## Masked View Setup\n\nRequired for `SharedIGImage` and `SharedAppleMusic` presets. The masked view creates the \"reveal\" effect where content expands from the shared element.\n\n\u003e **Note**: Requires native code. Will not work in Expo Go.\n\n### Installation\n\n```bash\n# Expo\nnpx expo install @react-native-masked-view/masked-view\n\n# Bare React Native\nnpm install @react-native-masked-view/masked-view\ncd ios \u0026\u0026 pod install\n```\n\n### Full Example\n\n**1. Source Screen** – Tag pressable elements:\n\n```tsx\n// app/index.tsx\nimport { router } from \"expo-router\";\nimport { View } from \"react-native\";\nimport Transition from \"react-native-screen-transitions\";\n\nexport default function HomeScreen() {\n  return (\n    \u003cView style={{ flex: 1, alignItems: \"center\", justifyContent: \"center\" }}\u003e\n      \u003cTransition.Pressable\n        sharedBoundTag=\"album-art\"\n        style={{\n          width: 200,\n          height: 200,\n          backgroundColor: \"#1DB954\",\n          borderRadius: 12,\n        }}\n        onPress={() =\u003e {\n          router.push({\n            pathname: \"/details\",\n            params: { sharedBoundTag: \"album-art\" },\n          });\n        }}\n      /\u003e\n    \u003c/View\u003e\n  );\n}\n```\n\n**2. Destination Screen** – Wrap with MaskedView and match the tag:\n\n```tsx\n// app/details.tsx\nimport { useLocalSearchParams } from \"expo-router\";\nimport Transition from \"react-native-screen-transitions\";\n\nexport default function DetailsScreen() {\n  const { sharedBoundTag } = useLocalSearchParams\u003c{ sharedBoundTag: string }\u003e();\n\n  return (\n    \u003cTransition.MaskedView style={{ flex: 1, backgroundColor: \"#121212\" }}\u003e\n      \u003cTransition.View\n        sharedBoundTag={sharedBoundTag}\n        style={{\n          backgroundColor: \"#1DB954\",\n          width: 400,\n          height: 400,\n          alignSelf: \"center\",\n          borderRadius: 12,\n        }}\n      /\u003e\n      {/* Additional screen content */}\n    \u003c/Transition.MaskedView\u003e\n  );\n}\n```\n\n**3. Layout** – Apply the preset with dynamic tag:\n\n```tsx\n// app/_layout.tsx\nimport Transition from \"react-native-screen-transitions\";\nimport { Stack } from \"./stack\";\n\nexport default function RootLayout() {\n  return (\n    \u003cStack\u003e\n      \u003cStack.Screen name=\"index\" /\u003e\n      \u003cStack.Screen\n        name=\"details\"\n        options={({ route }) =\u003e ({\n          ...Transition.Presets.SharedAppleMusic({\n            sharedBoundTag: route.params?.sharedBoundTag ?? \"\",\n          }),\n        })}\n      /\u003e\n    \u003c/Stack\u003e\n  );\n}\n```\n\n### How It Works\n\n1. `Transition.Pressable` measures its bounds on press and stores them with the tag\n2. `Transition.View` on the destination registers as the target for that tag\n3. `Transition.MaskedView` clips content to the animating shared element bounds\n4. The preset interpolates position, size, and mask for a seamless expand/collapse effect\n\n---\n\n## Support\n\nThis package is developed in my spare time.\n\nIf you'd like to fuel the next release, [buy me a coffee](https://buymeacoffee.com/trpfsu)\n\n## License\n\nMIT\n","funding_links":["https://buymeacoffee.com/trpfsu"],"categories":["Animation \u0026 Gestures"],"sub_categories":["Graphics \u0026 Drawing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feds2002%2Freact-native-screen-transitions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feds2002%2Freact-native-screen-transitions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feds2002%2Freact-native-screen-transitions/lists"}