react-native-screen-transitions 3.0.0-rc.2 → 3.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +421 -371
  2. package/lib/commonjs/blank-stack/components/{Overlay.js → overlay.js} +7 -5
  3. package/lib/commonjs/blank-stack/components/overlay.js.map +1 -0
  4. package/lib/commonjs/blank-stack/components/{Screens.js → screens.js} +8 -10
  5. package/lib/commonjs/blank-stack/components/screens.js.map +1 -0
  6. package/lib/commonjs/blank-stack/components/stack-view.js +95 -0
  7. package/lib/commonjs/blank-stack/components/stack-view.js.map +1 -0
  8. package/lib/commonjs/blank-stack/index.js +1 -8
  9. package/lib/commonjs/blank-stack/index.js.map +1 -1
  10. package/lib/commonjs/blank-stack/navigators/{createBlankStackNavigator.js → create-blank-stack-navigator.js} +3 -3
  11. package/lib/commonjs/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -0
  12. package/lib/commonjs/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js +1 -11
  13. package/lib/commonjs/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js.map +1 -1
  14. package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js +1 -12
  15. package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js.map +1 -1
  16. package/lib/commonjs/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.js.map +1 -1
  17. package/lib/commonjs/blank-stack/utils/with-stack-navigation/index.js +49 -55
  18. package/lib/commonjs/blank-stack/utils/with-stack-navigation/index.js.map +1 -1
  19. package/lib/commonjs/blank-stack/utils/with-stack-navigation/{_types.js → types.js} +1 -1
  20. package/lib/commonjs/blank-stack/utils/with-stack-navigation/types.js.map +1 -0
  21. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js +38 -22
  22. package/lib/commonjs/shared/hooks/animation/use-screen-animation.js.map +1 -1
  23. package/lib/commonjs/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  24. package/lib/commonjs/shared/providers/flags.provider.js +25 -0
  25. package/lib/commonjs/shared/providers/flags.provider.js.map +1 -0
  26. package/lib/commonjs/shared/providers/register-bounds.provider.js +71 -45
  27. package/lib/commonjs/shared/providers/register-bounds.provider.js.map +1 -1
  28. package/lib/commonjs/shared/stores/bounds.store.js +91 -47
  29. package/lib/commonjs/shared/stores/bounds.store.js.map +1 -1
  30. package/lib/commonjs/shared/utils/bounds/helpers/is-bounds-equal.js +1 -1
  31. package/lib/commonjs/shared/utils/bounds/helpers/is-bounds-equal.js.map +1 -1
  32. package/lib/commonjs/shared/utils/bounds/index.js +4 -5
  33. package/lib/commonjs/shared/utils/bounds/index.js.map +1 -1
  34. package/lib/commonjs/shared/utils/create-provider.js +20 -1
  35. package/lib/commonjs/shared/utils/create-provider.js.map +1 -1
  36. package/lib/commonjs/shared/utils/reset-stores-for-screen.js +2 -0
  37. package/lib/commonjs/shared/utils/reset-stores-for-screen.js.map +1 -1
  38. package/lib/module/blank-stack/components/{Overlay.js → overlay.js} +7 -5
  39. package/lib/module/blank-stack/components/overlay.js.map +1 -0
  40. package/lib/module/blank-stack/components/{Screens.js → screens.js} +8 -10
  41. package/lib/module/blank-stack/components/screens.js.map +1 -0
  42. package/lib/module/blank-stack/components/stack-view.js +90 -0
  43. package/lib/module/blank-stack/components/stack-view.js.map +1 -0
  44. package/lib/module/blank-stack/index.js +1 -2
  45. package/lib/module/blank-stack/index.js.map +1 -1
  46. package/lib/module/blank-stack/navigators/{createBlankStackNavigator.js → create-blank-stack-navigator.js} +2 -2
  47. package/lib/module/blank-stack/navigators/create-blank-stack-navigator.js.map +1 -0
  48. package/lib/module/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js +1 -11
  49. package/lib/module/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.js.map +1 -1
  50. package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js +1 -12
  51. package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.js.map +1 -1
  52. package/lib/module/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.js.map +1 -1
  53. package/lib/module/blank-stack/utils/with-stack-navigation/index.js +48 -54
  54. package/lib/module/blank-stack/utils/with-stack-navigation/index.js.map +1 -1
  55. package/lib/module/blank-stack/utils/with-stack-navigation/types.js +4 -0
  56. package/lib/module/blank-stack/utils/with-stack-navigation/types.js.map +1 -0
  57. package/lib/module/shared/hooks/animation/use-screen-animation.js +38 -22
  58. package/lib/module/shared/hooks/animation/use-screen-animation.js.map +1 -1
  59. package/lib/module/shared/hooks/gestures/use-build-gestures.js.map +1 -1
  60. package/lib/module/shared/providers/flags.provider.js +19 -0
  61. package/lib/module/shared/providers/flags.provider.js.map +1 -0
  62. package/lib/module/shared/providers/register-bounds.provider.js +71 -45
  63. package/lib/module/shared/providers/register-bounds.provider.js.map +1 -1
  64. package/lib/module/shared/stores/bounds.store.js +91 -47
  65. package/lib/module/shared/stores/bounds.store.js.map +1 -1
  66. package/lib/module/shared/utils/bounds/helpers/is-bounds-equal.js +1 -1
  67. package/lib/module/shared/utils/bounds/helpers/is-bounds-equal.js.map +1 -1
  68. package/lib/module/shared/utils/bounds/index.js +4 -5
  69. package/lib/module/shared/utils/bounds/index.js.map +1 -1
  70. package/lib/module/shared/utils/create-provider.js +20 -1
  71. package/lib/module/shared/utils/create-provider.js.map +1 -1
  72. package/lib/module/shared/utils/reset-stores-for-screen.js +2 -0
  73. package/lib/module/shared/utils/reset-stores-for-screen.js.map +1 -1
  74. package/lib/typescript/blank-stack/components/{Overlay.d.ts → overlay.d.ts} +1 -1
  75. package/lib/typescript/blank-stack/components/overlay.d.ts.map +1 -0
  76. package/lib/typescript/blank-stack/components/{Screens.d.ts → screens.d.ts} +1 -1
  77. package/lib/typescript/blank-stack/components/{Screens.d.ts.map → screens.d.ts.map} +1 -1
  78. package/lib/typescript/blank-stack/components/stack-view.d.ts +3 -0
  79. package/lib/typescript/blank-stack/components/stack-view.d.ts.map +1 -0
  80. package/lib/typescript/blank-stack/index.d.ts +1 -2
  81. package/lib/typescript/blank-stack/index.d.ts.map +1 -1
  82. package/lib/typescript/blank-stack/navigators/{createBlankStackNavigator.d.ts → create-blank-stack-navigator.d.ts} +1 -1
  83. package/lib/typescript/blank-stack/navigators/create-blank-stack-navigator.d.ts.map +1 -0
  84. package/lib/typescript/blank-stack/types.d.ts +4 -0
  85. package/lib/typescript/blank-stack/types.d.ts.map +1 -1
  86. package/lib/typescript/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.d.ts.map +1 -1
  87. package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.d.ts.map +1 -1
  88. package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.d.ts +1 -1
  89. package/lib/typescript/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.d.ts.map +1 -1
  90. package/lib/typescript/blank-stack/utils/with-stack-navigation/index.d.ts +3 -5
  91. package/lib/typescript/blank-stack/utils/with-stack-navigation/index.d.ts.map +1 -1
  92. package/lib/typescript/blank-stack/utils/with-stack-navigation/{_types.d.ts → types.d.ts} +1 -1
  93. package/lib/typescript/blank-stack/utils/with-stack-navigation/types.d.ts.map +1 -0
  94. package/lib/typescript/shared/hooks/animation/use-screen-animation.d.ts.map +1 -1
  95. package/lib/typescript/shared/hooks/gestures/use-build-gestures.d.ts.map +1 -1
  96. package/lib/typescript/shared/index.d.ts +20 -20
  97. package/lib/typescript/shared/providers/flags.provider.d.ts +10 -0
  98. package/lib/typescript/shared/providers/flags.provider.d.ts.map +1 -0
  99. package/lib/typescript/shared/providers/register-bounds.provider.d.ts.map +1 -1
  100. package/lib/typescript/shared/stores/bounds.store.d.ts +23 -11
  101. package/lib/typescript/shared/stores/bounds.store.d.ts.map +1 -1
  102. package/lib/typescript/shared/types/bounds.types.d.ts +2 -2
  103. package/lib/typescript/shared/types/bounds.types.d.ts.map +1 -1
  104. package/lib/typescript/shared/utils/bounds/index.d.ts.map +1 -1
  105. package/lib/typescript/shared/utils/create-provider.d.ts +2 -2
  106. package/lib/typescript/shared/utils/create-provider.d.ts.map +1 -1
  107. package/lib/typescript/shared/utils/reset-stores-for-screen.d.ts.map +1 -1
  108. package/package.json +2 -1
  109. package/src/blank-stack/components/{Overlay.tsx → overlay.tsx} +4 -3
  110. package/src/blank-stack/components/{Screens.tsx → screens.tsx} +7 -9
  111. package/src/blank-stack/components/stack-view.tsx +104 -0
  112. package/src/blank-stack/index.ts +1 -2
  113. package/src/blank-stack/navigators/{createBlankStackNavigator.tsx → create-blank-stack-navigator.tsx} +1 -1
  114. package/src/blank-stack/types.ts +5 -7
  115. package/src/blank-stack/utils/with-stack-navigation/helpers/compose-descriptors.ts +1 -8
  116. package/src/blank-stack/utils/with-stack-navigation/hooks/use-closing-route-keys.tsx +1 -12
  117. package/src/blank-stack/utils/with-stack-navigation/hooks/use-stack-navigation-state.tsx +1 -1
  118. package/src/blank-stack/utils/with-stack-navigation/index.tsx +42 -62
  119. package/src/shared/__tests__/bounds.store.test.ts +398 -167
  120. package/src/shared/__tests__/determine-dismissal.test.ts +2 -12
  121. package/src/shared/__tests__/geometry.test.ts +1 -1
  122. package/src/shared/__tests__/gesture.velocity.test.ts +2 -10
  123. package/src/shared/hooks/animation/use-screen-animation.tsx +55 -29
  124. package/src/shared/hooks/gestures/use-build-gestures.tsx +4 -1
  125. package/src/shared/providers/flags.provider.tsx +21 -0
  126. package/src/shared/providers/register-bounds.provider.tsx +85 -54
  127. package/src/shared/stores/bounds.store.ts +90 -54
  128. package/src/shared/types/bounds.types.ts +2 -2
  129. package/src/shared/utils/bounds/helpers/is-bounds-equal.ts +1 -1
  130. package/src/shared/utils/bounds/index.ts +7 -10
  131. package/src/shared/utils/create-provider.tsx +35 -1
  132. package/src/shared/utils/reset-stores-for-screen.ts +2 -0
  133. package/lib/commonjs/blank-stack/components/Overlay.js.map +0 -1
  134. package/lib/commonjs/blank-stack/components/Screens.js.map +0 -1
  135. package/lib/commonjs/blank-stack/components/StackView.js +0 -93
  136. package/lib/commonjs/blank-stack/components/StackView.js.map +0 -1
  137. package/lib/commonjs/blank-stack/navigators/createBlankStackNavigator.js.map +0 -1
  138. package/lib/commonjs/blank-stack/utils/with-stack-navigation/_types.js.map +0 -1
  139. package/lib/module/blank-stack/components/Overlay.js.map +0 -1
  140. package/lib/module/blank-stack/components/Screens.js.map +0 -1
  141. package/lib/module/blank-stack/components/StackView.js +0 -88
  142. package/lib/module/blank-stack/components/StackView.js.map +0 -1
  143. package/lib/module/blank-stack/navigators/createBlankStackNavigator.js.map +0 -1
  144. package/lib/module/blank-stack/utils/with-stack-navigation/_types.js +0 -4
  145. package/lib/module/blank-stack/utils/with-stack-navigation/_types.js.map +0 -1
  146. package/lib/typescript/blank-stack/components/Overlay.d.ts.map +0 -1
  147. package/lib/typescript/blank-stack/components/StackView.d.ts +0 -2
  148. package/lib/typescript/blank-stack/components/StackView.d.ts.map +0 -1
  149. package/lib/typescript/blank-stack/navigators/createBlankStackNavigator.d.ts.map +0 -1
  150. package/lib/typescript/blank-stack/utils/with-stack-navigation/_types.d.ts.map +0 -1
  151. package/src/blank-stack/components/StackView.tsx +0 -108
  152. /package/src/blank-stack/utils/with-stack-navigation/{_types.ts → types.ts} +0 -0
package/README.md CHANGED
@@ -1,34 +1,27 @@
1
1
  # react-native-screen-transitions
2
2
 
3
- > ⚠️ **Work In Progress** ⚠️
4
- > This documentation is for **v3 beta 10**. API and features may change.
5
-
3
+ Customizable screen transitions for React Native. Build gesture-driven, shared element, and fully custom animations with a simple API.
6
4
 
7
5
  | iOS | Android |
8
6
  | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
9
7
  | <video src="https://github.com/user-attachments/assets/c0d17b8f-7268-421c-9051-e242f8ddca76" width="300" height="600" controls></video> | <video src="https://github.com/user-attachments/assets/3f8d5fb1-96d2-4fe3-860d-62f6fb5a687e" width="300" controls></video> |
10
8
 
11
- ## Features
9
+ ## Features
12
10
 
13
- - 🎯 **Reanimated v3-4 Compatible** – Built for the latest React Native Reanimated
14
- - 📱 **Cross-Platform**Supports iOS and Android (web not supported)
15
- - 🔷 **TypeScript First** – Fully typed for better development experience
16
- - 👆 **Advanced Gestures** – Powered by react-native-gesture-handler with edge and screen activation areas
17
- - 🧭 **Navigation Ready** – Works seamlessly with expo-router and react-navigation
18
- - 🔗 **Shared Elements** – Bounds API for measure-driven transitions between screens
19
- - 🎭 **Ready-Made Presets** – Instagram, Apple Music, X (Twitter) style transitions included
11
+ - **Full Animation Control** – Define exactly how screens enter, exit, and respond to gestures
12
+ - **Shared Elements** – Measure-driven transitions between screens using the Bounds API
13
+ - **Gesture Support** – Swipe-to-dismiss with edge or full-screen activation, works with ScrollViews
14
+ - **Two Stack Options** – Pure JS stack (recommended) or native stack integration
15
+ - **Ready-Made Presets** – Instagram, Apple Music, X (Twitter) style transitions included
16
+ - **TypeScript First** – Fully typed for better development experience
20
17
 
21
18
  ## Installation
22
19
 
23
20
  ```bash
24
21
  npm install react-native-screen-transitions
25
- # or
26
- yarn add react-native-screen-transitions
27
- # or
28
- bun add react-native-screen-transitions
29
22
  ```
30
23
 
31
- ## Peer Dependencies
24
+ ### Peer Dependencies
32
25
 
33
26
  ```bash
34
27
  npm install react-native-reanimated react-native-gesture-handler \
@@ -37,9 +30,42 @@ npm install react-native-reanimated react-native-gesture-handler \
37
30
  react-native-safe-area-context
38
31
  ```
39
32
 
40
- ## Setup
33
+ ---
34
+
35
+ ## Quick Start
36
+
37
+ This package provides two stack navigators:
38
+
39
+ | Stack | Description |
40
+ |-------|-------------|
41
+ | **Blank Stack** (recommended) | Pure JavaScript stack with full control over transitions, overlays, and gestures. No native limitations. |
42
+ | **Native Stack** | Extends `@react-navigation/native-stack`. More limited due to native constraints. |
41
43
 
42
- ### 1. Expo Router
44
+ ### Blank Stack Setup
45
+
46
+ ```tsx
47
+ import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
48
+ import Transition from "react-native-screen-transitions";
49
+
50
+ const Stack = createBlankStackNavigator();
51
+
52
+ function App() {
53
+ return (
54
+ <Stack.Navigator>
55
+ <Stack.Screen name="Home" component={HomeScreen} />
56
+ <Stack.Screen
57
+ name="Detail"
58
+ component={DetailScreen}
59
+ options={{
60
+ ...Transition.Presets.SlideFromBottom(),
61
+ }}
62
+ />
63
+ </Stack.Navigator>
64
+ );
65
+ }
66
+ ```
67
+
68
+ ### Blank Stack with Expo Router
43
69
 
44
70
  ```tsx
45
71
  import type {
@@ -48,506 +74,530 @@ import type {
48
74
  } from "@react-navigation/native";
49
75
  import { withLayoutContext } from "expo-router";
50
76
  import {
51
- createNativeStackNavigator,
52
- type NativeStackNavigationEventMap,
53
- type NativeStackNavigationOptions,
54
- } from "react-native-screen-transitions/native-stack";
77
+ createBlankStackNavigator,
78
+ type BlankStackNavigationEventMap,
79
+ type BlankStackNavigationOptions,
80
+ } from "react-native-screen-transitions/blank-stack";
55
81
 
56
- const { Navigator } = createNativeStackNavigator();
82
+ const { Navigator } = createBlankStackNavigator();
57
83
 
58
84
  export const Stack = withLayoutContext<
59
- NativeStackNavigationOptions,
85
+ BlankStackNavigationOptions,
60
86
  typeof Navigator,
61
87
  StackNavigationState<ParamListBase>,
62
- NativeStackNavigationEventMap
88
+ BlankStackNavigationEventMap
63
89
  >(Navigator);
64
90
  ```
65
91
 
66
- That’s it — you’re ready to go.
92
+ ---
67
93
 
68
- ### 2. React Navigation (bare)
94
+ ## Presets
69
95
 
70
- If you’re using **React Navigation** directly (not Expo Router), the navigator is already configured.
71
- No extra setup is required—just import and use as usual:
96
+ Built-in animation presets you can spread into screen options:
72
97
 
73
98
  ```tsx
74
- import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";
75
-
76
- const Stack = createNativeStackNavigator();
77
-
78
- // Use Stack.Navigator and Stack.Screen as normal
99
+ <Stack.Screen
100
+ name="Detail"
101
+ options={{
102
+ ...Transition.Presets.SlideFromBottom(),
103
+ }}
104
+ />
79
105
  ```
80
106
 
81
- ### Extended native-stack options
82
-
83
- This package ships an **extended native stack** built on top of React Navigation’s native stack.
84
- All the usual native-stack options are available, plus the following extras:
85
-
86
- | Option | Type | Description |
87
- | ------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------- | ----- | --- | -------------- | ------------ |
88
- | `enableTransitions` | `boolean` | Switches the screen to a transparent modal and disables the header so custom transitions can take over. |
89
- | `screenStyleInterpolator` | `ScreenStyleInterpolator` | Function that returns animated styles based on transition progress. |
90
- | `transitionSpec` | `TransitionSpec` | Reanimated timing/spring config for open/close animations. |
91
- | `gestureEnabled` | `boolean` | Whether swipe-to-dismiss is allowed. |
92
- | `gestureDirection` | `GestureDirection \| GestureDirection[]` | Allowed swipe directions (`vertical`, `horizontal`, etc.). |
93
- | `gestureVelocityImpact` | `number` | How much the gesture’s velocity affects dismissal. |
94
- | `gestureResponseDistance` | `number` | Distance from screen where the gesture is recognized. |
95
- | `gestureDrivesProgress` | `boolean` | Whether the gesture directly drives the transition progress. |
96
- | `gestureActivationArea` | `GestureActivationArea` | Where a gesture may start. `'edge' | 'screen'`or per-side`{ left | right | top | bottom: 'edge' | 'screen' }`. |
97
-
98
- ### Renamed native options (extended stack)
99
-
100
- To avoid collisions with the new options above, the built-in React Navigation gesture props are renamed:
107
+ | Preset | Description |
108
+ |--------|-------------|
109
+ | `SlideFromTop()` | Slides in from top, vertical gesture dismiss |
110
+ | `SlideFromBottom()` | Slides in from bottom, vertical gesture dismiss |
111
+ | `ZoomIn()` | Scales in with fade, no gesture |
112
+ | `DraggableCard()` | Multi-directional drag with card scaling |
113
+ | `ElasticCard()` | Elastic drag with overlay darkening |
114
+ | `SharedIGImage({ sharedBoundTag })` | Instagram-style shared image transition |
115
+ | `SharedAppleMusic({ sharedBoundTag })` | Apple Music-style shared element |
116
+ | `SharedXImage({ sharedBoundTag })` | X (Twitter)-style image transition |
101
117
 
102
- | React Navigation prop | Renamed to |
103
- | ------------------------- | ------------------------------- |
104
- | `gestureDirection` | `nativeGestureDirection` |
105
- | `gestureEnabled` | `nativeGestureEnabled` |
106
- | `gestureResponseDistance` | `nativeGestureResponseDistance` |
107
-
108
- All other React Navigation native-stack options keep their original names.
109
-
110
- ## Blank Stack (New in v3)
118
+ ---
111
119
 
112
- v3 introduces `createBlankStackNavigator`, a pure JavaScript stack inspired by `react-navigation/stack`.
113
- Unlike `native-stack`, it does not rely on native screen primitives for transitions, giving you full control over animations without fighting the OS.
120
+ ## Custom Animations
114
121
 
115
- **Philosophy:**
116
- - **"Blank" Canvas:** No default OS-style animations. You build or choose your transitions.
117
- - **High Performance:** Logic is handled in JS (Reanimated), avoiding native-side interruptions.
118
- - **No Hacks:** Does not use transparent modals or `beforeRemove` listeners.
122
+ ### Using `screenStyleInterpolator`
119
123
 
120
- ### Usage
124
+ Define custom transitions directly in screen options. The interpolator receives animation state and returns styles:
121
125
 
122
126
  ```tsx
123
- import { createBlankStackNavigator } from "react-native-screen-transitions/blank-stack";
124
-
125
- const Stack = createBlankStackNavigator();
126
-
127
- function App() {
128
- return (
129
- <Stack.Navigator screenOptions={{ ...Transition.Presets.SlideFromBottom() }}>
130
- <Stack.Screen name="Home" component={HomeScreen} />
131
- <Stack.Screen name="Detail" component={DetailScreen} />
132
- </Stack.Navigator>
133
- );
134
- }
135
- ```
136
-
137
- ### Overlay
138
-
139
- The Blank Stack introduces a powerful `overlay` prop for animating headers, footers, or floating elements that persist or transition between screens.
140
-
141
- - **Modes:**
142
- - `float`: Persists above the stack (like a native header). Smartly handles transitions so it doesn't disappear prematurely.
143
- - `screen`: Moves with the screen content.
127
+ import { interpolate } from "react-native-reanimated";
144
128
 
145
- ```tsx
146
129
  <Stack.Screen
147
- name="Profile"
130
+ name="Detail"
148
131
  options={{
149
- overlayShown: true,
150
- overlayMode: 'float',
151
- overlay: ({ overlayAnimation, insets }) => (
152
- // Your custom header
153
- <Header progress={overlayAnimation} top={insets.top} />
154
- )
132
+ screenStyleInterpolator: ({ progress, layouts: { screen } }) => {
133
+ "worklet";
134
+
135
+ const translateX = interpolate(
136
+ progress,
137
+ [0, 1, 2],
138
+ [screen.width, 0, -screen.width]
139
+ );
140
+
141
+ return {
142
+ contentStyle: {
143
+ transform: [{ translateX }],
144
+ },
145
+ };
146
+ },
147
+ transitionSpec: {
148
+ open: Transition.Specs.DefaultSpec,
149
+ close: Transition.Specs.DefaultSpec,
150
+ },
155
151
  }}
156
- />
152
+ />;
157
153
  ```
158
154
 
159
- ## Creating your screen animations
155
+ ### Interpolator Props
160
156
 
161
- ### Using presets
157
+ | Prop | Description |
158
+ |------|-------------|
159
+ | `progress` | Combined progress (0-2). 0=entering, 1=active, 2=exiting |
160
+ | `current` | Current screen state (progress, closing, gesture, route) |
161
+ | `previous` | Previous screen state (may be undefined) |
162
+ | `next` | Next screen state (may be undefined) |
163
+ | `layouts.screen` | Screen dimensions `{ width, height }` |
164
+ | `insets` | Safe area insets `{ top, right, bottom, left }` |
165
+ | `focused` | Whether current screen is the topmost |
166
+ | `active` | The screen driving the transition |
167
+ | `isActiveTransitioning` | Whether active screen is animating |
168
+ | `isDismissing` | Whether active screen is being dismissed |
169
+ | `bounds` | Function to access shared element positions |
162
170
 
163
- Pick a built-in preset and spread it into the screen’s options.
164
- The incoming screen automatically controls the previous screen.
171
+ ### Return Value
165
172
 
166
173
  ```tsx
167
- <Stack>
168
- <Stack.Screen name="a" />
169
- <Stack.Screen
170
- name="b"
171
- options={{
172
- ...Transition.Presets.SlideFromTop(),
173
- }}
174
- />
175
- <Stack.Screen
176
- name="c"
177
- options={{
178
- ...Transition.Presets.SlideFromBottom(),
179
- }}
180
- />
181
- </Stack>
174
+ return {
175
+ contentStyle: { ... }, // Main screen content
176
+ overlayStyle: { ... }, // Semi-transparent overlay
177
+ ["my-element"]: { ... }, // Styles for Transition.View with styleId="my-element"
178
+ };
182
179
  ```
183
180
 
184
- #### Shared element presets (new)
181
+ ### Using `styleId` for Individual Elements
185
182
 
186
- Ready-made presets for common shared-element patterns. These leverage the bounds API under the hood. Tag your views with `sharedBoundTag` on both screens.
183
+ Animate specific elements within a screen:
187
184
 
188
185
  ```tsx
189
- <Stack.Screen name="feed" />
190
- <Stack.Screen
191
- name="post"
192
- options={{
193
- ...Transition.Presets.SharedIGImage(),
194
- }}
195
- />
186
+ // In screen options
187
+ screenStyleInterpolator: ({ progress }) => {
188
+ "worklet";
189
+ return {
190
+ "hero-image": {
191
+ opacity: interpolate(progress, [0, 1], [0, 1]),
192
+ transform: [{ scale: interpolate(progress, [0, 1], [0.8, 1]) }],
193
+ },
194
+ };
195
+ };
196
+
197
+ // In component
198
+ <Transition.View styleId="hero-image">
199
+ <Image source={...} />
200
+ </Transition.View>
196
201
  ```
197
202
 
198
- Other presets: `SharedAppleMusic()`, `SharedXImage()`.
203
+ ---
199
204
 
200
- #### 🎭 Masked View Setup (Required for SharedIGImage & SharedAppleMusic)
205
+ ## Shared Elements (Bounds API)
201
206
 
202
- > **⚠️ Important**: These presets require native code and **will not work in Expo Go**. You must use a development build.
207
+ Animate elements between screens by measuring their positions.
203
208
 
204
- **1. Install the dependency**
209
+ ### 1. Tag Elements on Both Screens
205
210
 
206
- ```bash
207
- # Expo projects
208
- npx expo install @react-native-masked-view/masked-view
211
+ ```tsx
212
+ // Source screen
213
+ <Transition.Pressable
214
+ sharedBoundTag="avatar"
215
+ onPress={() => navigation.navigate("Profile")}
216
+ >
217
+ <Image source={avatar} style={{ width: 50, height: 50 }} />
218
+ </Transition.Pressable>
209
219
 
210
- # Bare React Native
211
- npm install @react-native-masked-view/masked-view
212
- cd ios && pod install # iOS only
220
+ // Destination screen
221
+ <Transition.View sharedBoundTag="avatar">
222
+ <Image source={avatar} style={{ width: 200, height: 200 }} />
223
+ </Transition.View>
213
224
  ```
214
225
 
215
- **2. Create a development build** (if using Expo)
226
+ ### 2. Use Bounds in Interpolator
216
227
 
217
- ```bash
218
- npx expo run:ios
219
- # or
220
- npx expo run:android
228
+ ```tsx
229
+ screenStyleInterpolator: ({ bounds }) => {
230
+ "worklet";
231
+
232
+ const avatarStyles = bounds({
233
+ id: "avatar",
234
+ method: "transform", // "transform" | "size" | "content"
235
+ space: "relative", // "relative" | "absolute"
236
+ scaleMode: "match", // "match" | "none" | "uniform"
237
+ anchor: "center", // positioning anchor
238
+ });
239
+
240
+ return {
241
+ avatar: avatarStyles,
242
+ };
243
+ };
221
244
  ```
222
245
 
223
- **3. Wrap your destination screen**
246
+ ### Bounds Options
247
+
248
+ | Option | Values | Description |
249
+ |--------|--------|-------------|
250
+ | `id` | string | The `sharedBoundTag` to match |
251
+ | `method` | `"transform"` `"size"` `"content"` | How to animate (scale vs width/height) |
252
+ | `space` | `"relative"` `"absolute"` | Coordinate space |
253
+ | `scaleMode` | `"match"` `"none"` `"uniform"` | How to handle aspect ratio |
254
+ | `anchor` | `"center"` `"top"` `"topLeading"` etc. | Transform origin |
255
+ | `target` | `"bound"` `"fullscreen"` or custom | Destination target |
256
+ | `raw` | boolean | Return raw values instead of styles |
257
+
258
+ ### Raw Values
224
259
 
225
260
  ```tsx
226
- export default function PostScreen() {
227
- return (
228
- <Transition.MaskedView style={{ flex: 1, backgroundColor: "white" }}>
229
- {/* screen content, including the destination bound */}
230
- </Transition.MaskedView>
231
- );
232
- }
261
+ const raw = bounds({ id: "avatar", method: "transform", raw: true });
262
+ // { translateX, translateY, scaleX, scaleY }
233
263
  ```
234
264
 
235
- > **💡 Fallback behavior**: `Transition.MaskedView` will fall back to a plain `View` if the masked view library is missing, but this breaks the shared element effect and may cause errors like "bounds is not a function".
236
-
237
265
  ---
238
266
 
239
- ### Navigator-level custom animations
267
+ ## Gestures
240
268
 
241
- Instead of presets, you can define a custom transition directly on the screen's options.
242
- `screenStyleInterpolator` receives an object with the following useful fields:
243
-
244
- - `progress` – combined progress of current and next screen transitions, ranging from 0-2.
245
- - `current` – state for the current screen being interpolated (includes `progress`, `closing`, `gesture`, `route`, etc.).
246
- - `previous` – state for the screen that came before the current one in the navigation stack (may be `undefined`).
247
- - `next` – state for the screen that comes after the current one in the navigation stack (may be `undefined`).
248
- - `layouts.screen` – `{ width, height }` of the container.
249
- - `insets` – `{ top, right, bottom, left }` safe-area insets.
250
- - `bounds(options)` – function that provides access to bounds builders for creating shared element transitions. See "Bounds" below.
251
- - `activeBoundId` – ID of the currently active shared bound (e.g., 'a' when Transition.Pressable has sharedBoundTag='a').
252
- - `focused` – whether the current screen is the focused (topmost) screen in the stack.
253
- - `active` – the screen state that is currently driving the transition (either current or next, whichever is focused).
254
- - `isActiveTransitioning` – whether the active screen is currently transitioning (either being dragged or animating).
255
- - `isDismissing` – whether the active screen is in the process of being dismissed/closed.
269
+ Enable swipe-to-dismiss on screens:
256
270
 
257
271
  ```tsx
258
- import { interpolate } from "react-native-reanimated";
259
-
260
272
  <Stack.Screen
261
- name="b"
273
+ name="Detail"
262
274
  options={{
263
- enableTransitions: true,
264
- screenStyleInterpolator: ({
265
- layouts: {
266
- screen: { width },
267
- },
268
- progress,
269
- }) => {
270
- "worklet";
271
-
272
- const x = interpolate(progress, [0, 1, 2], [width, 0, -width]);
273
- return {
274
- contentStyle: {
275
- transform: [{ translateX: x }],
276
- },
277
- };
278
- },
279
- transitionSpec: {
280
- close: Transition.Specs.DefaultSpec,
281
- open: Transition.Specs.DefaultSpec,
282
- },
275
+ gestureEnabled: true,
276
+ gestureDirection: "vertical", // or "horizontal", ["vertical", "horizontal"]
277
+ gestureActivationArea: "edge", // or "screen", or { left: "edge", top: "screen" }
278
+ gestureResponseDistance: 50,
279
+ gestureVelocityImpact: 0.3,
283
280
  }}
284
- />;
281
+ />
285
282
  ```
286
283
 
287
- In this example the incoming screen slides in from the right while the exiting screen slides out to the left.
284
+ ### Gesture Options
288
285
 
289
- ### Screen-level custom animations with `useScreenAnimation`
286
+ | Option | Description |
287
+ |--------|-------------|
288
+ | `gestureEnabled` | Enable/disable gesture |
289
+ | `gestureDirection` | `"horizontal"` `"vertical"` `"horizontal-inverted"` `"vertical-inverted"` or array |
290
+ | `gestureActivationArea` | `"edge"` `"screen"` or per-side config |
291
+ | `gestureResponseDistance` | Distance threshold for gesture recognition |
292
+ | `gestureVelocityImpact` | How much velocity affects dismissal decision |
293
+ | `gestureDrivesProgress` | Whether gesture directly drives animation (default: true) |
290
294
 
291
- For per-screen control, import the `useScreenAnimation` hook and compose your own animated styles.
295
+ ### Gestures with ScrollViews
292
296
 
293
- ```tsx
294
- import { useScreenAnimation } from "react-native-screen-transitions";
295
- import Animated, {
296
- useAnimatedStyle,
297
- interpolate,
298
- } from "react-native-reanimated";
299
-
300
- export default function BScreen() {
301
- const props = useScreenAnimation();
302
-
303
- const animatedStyle = useAnimatedStyle(() => {
304
- const {
305
- current: { progress },
306
- } = props.value;
307
- return {
308
- opacity: progress,
309
- };
310
- });
311
-
312
- return (
313
- <Animated.View style={[{ flex: 1 }, animatedStyle]}>
314
- {/* Your content */}
315
- </Animated.View>
316
- );
317
- }
318
- ```
319
-
320
- ## Swipe-to-dismiss with scrollables
321
-
322
- You can drag a screen away even when it contains a scroll view.
323
- Just swap the regular scrollable for a transition-aware one:
297
+ Use transition-aware scrollables so gestures work correctly:
324
298
 
325
299
  ```tsx
326
300
  import Transition from "react-native-screen-transitions";
327
- import { LegendList } from "@legendapp/list";
328
- import { FlashList } from "@shopify/flash-list";
329
301
 
330
302
  // Drop-in replacements
331
- const ScrollView = Transition.ScrollView;
332
- const FlatList = Transition.FlatList;
303
+ <Transition.ScrollView>
304
+ {/* content */}
305
+ </Transition.ScrollView>
333
306
 
334
- // Or wrap any list you like
307
+ <Transition.FlatList
308
+ data={items}
309
+ renderItem={...}
310
+ />
311
+
312
+ // Wrap custom lists
335
313
  const TransitionFlashList = Transition.createTransitionAwareComponent(
336
314
  FlashList,
337
315
  { isScrollable: true }
338
316
  );
339
-
340
- const TransitionLegendList = Transition.createTransitionAwareComponent(
341
- LegendList,
342
- { isScrollable: true }
343
- );
344
317
  ```
345
318
 
346
- Enable the gesture on the screen:
319
+ Gesture rules with scrollables:
320
+ - **vertical** – only starts when scrolled to top
321
+ - **vertical-inverted** – only starts when scrolled to bottom
322
+ - **horizontal** – only starts at left/right edge
323
+
324
+ ---
325
+
326
+ ## Overlays (Blank Stack)
327
+
328
+ The Blank Stack supports persistent overlays that animate across screen transitions.
329
+
330
+ ### Float Overlay
331
+
332
+ A single overlay that persists above all screens:
347
333
 
348
334
  ```tsx
335
+ const FloatingHeader = ({ focusedIndex, routes, overlayAnimation }) => {
336
+ const style = useAnimatedStyle(() => ({
337
+ opacity: interpolate(overlayAnimation.value.progress, [0, 1], [0, 1]),
338
+ }));
339
+
340
+ return (
341
+ <Animated.View style={[styles.header, style]}>
342
+ <Text>Screen {focusedIndex + 1} of {routes.length}</Text>
343
+ </Animated.View>
344
+ );
345
+ };
346
+
349
347
  <Stack.Screen
350
- name="gallery"
348
+ name="Home"
351
349
  options={{
352
- enableTransitions: true,
353
- gestureEnabled: true,
354
- gestureDirection: "vertical", // or 'horizontal', ['vertical', 'horizontal'], etc.
350
+ overlay: FloatingHeader,
351
+ overlayMode: "float",
352
+ overlayShown: true,
355
353
  }}
356
354
  />
357
355
  ```
358
356
 
359
- Use it in the screen:
357
+ ### Screen Overlay
358
+
359
+ An overlay that moves with screen content:
360
360
 
361
361
  ```tsx
362
- export default function B() {
363
- return <Transition.ScrollView>{/* content */}</Transition.ScrollView>;
364
- }
362
+ <Stack.Screen
363
+ name="Detail"
364
+ options={{
365
+ overlay: DetailOverlay,
366
+ overlayMode: "screen",
367
+ overlayShown: true,
368
+ }}
369
+ />
365
370
  ```
366
371
 
367
- Gesture rules (handled automatically):
368
-
369
- - **vertical** – only starts when the list is at the very top
370
- - **vertical-inverted** – only starts when the list is at the very bottom
371
- - **horizontal** / **horizontal-inverted** – only starts when the list is at the left or right edge
372
-
373
- These rules apply **only when the screen contains a scrollable**.
374
- If no scroll view is present, the gesture can begin from **anywhere on the screen**—not restricted to the edges.
372
+ ### Overlay Props
375
373
 
376
- ### Gesture activation area
374
+ | Prop | Description |
375
+ |------|-------------|
376
+ | `focusedRoute` | Currently focused route |
377
+ | `focusedIndex` | Index of focused screen |
378
+ | `routes` | All routes in the stack |
379
+ | `overlayOptions` | Custom options passed from screen |
380
+ | `navigation` | Navigation prop |
381
+ | `overlayAnimation` | Animation values for overlay |
382
+ | `screenAnimation` | Animation values for screens |
377
383
 
378
- Control where gestures can start using `gestureActivationArea` on the screen options:
384
+ ### Passing Custom Data
379
385
 
380
386
  ```tsx
381
- // Gesture must start from any screen edge (all sides)
382
- gestureActivationArea: 'edge'
387
+ <Stack.Screen
388
+ options={{
389
+ overlay: MyOverlay,
390
+ overlayOptions: {
391
+ title: "Step 1",
392
+ showProgress: true,
393
+ },
394
+ }}
395
+ />
383
396
 
384
- // Allow vertical drags anywhere, horizontal drags only from the left edge
385
- gestureDirection: ['vertical', 'horizontal']
386
- gestureActivationArea: { top: 'screen', left: 'edge' }
397
+ // In overlay
398
+ const MyOverlay = ({ overlayOptions }) => {
399
+ return <Text>{overlayOptions.title}</Text>;
400
+ };
387
401
  ```
388
402
 
389
- ## Bounds (measure-driven screen transitions)
390
-
391
- > **⚠️ Breaking Change in v3**: The `sharedBoundTag` is now **strictly required** for all dynamic animations. The new architecture uses a Link Stack system that relies on this ID to correctly pair source and destination views. Without it, animations will fail.
403
+ ---
392
404
 
393
- Bounds let you animate any component between two screens by measuring its start and end positions. They are not shared elements — just measurements.
405
+ ## Transition Components
394
406
 
395
- **Measurement Behavior (v3)**: In v3, bounds are automatically measured **before a screen leaves the stack**. The trigger for measurement depends on the presence of an `onPress` handler on a `Transition.Pressable` component with a `sharedBoundTag`:
396
- * **With `onPress`**: If a `Transition.Pressable` has an `onPress` handler, it will automatically measure its bounds (and the bounds of any children with `sharedBoundTag`s) when pressed.
397
- * **Without `onPress`**: If a `Transition.Pressable` does *not* have an `onPress` handler, it will measure its bounds (and its children's) when the screen blurs (i.e., just before the screen transitions away).
407
+ | Component | Description |
408
+ |-----------|-------------|
409
+ | `Transition.View` | Animated view, supports `styleId` and `sharedBoundTag` |
410
+ | `Transition.Pressable` | Pressable with bounds measurement on press |
411
+ | `Transition.ScrollView` | ScrollView with gesture coordination |
412
+ | `Transition.FlatList` | FlatList with gesture coordination |
413
+ | `Transition.MaskedView` | For clipping during shared element transitions |
398
414
 
399
- 1. Tag source and destination with pressable triggers
415
+ ### Creating Custom Components
400
416
 
401
417
  ```tsx
402
- // Source screen
403
- <Transition.Pressable
404
- sharedBoundTag="hero"
405
- onPress={() => router.push('/detail')}
406
- style={{ width: 100, height: 100 }}
407
- >
408
- <Image source={...} />
409
- </Transition.Pressable>
410
-
411
- // Destination screen
412
- <Transition.Pressable
413
- sharedBoundTag="hero"
414
- onPress={() => {/* handle press */}}
415
- style={{ width: 200, height: 200 }}
416
- >
417
- <Image source={...} />
418
- </Transition.Pressable>
418
+ const TransitionImage = Transition.createTransitionAwareComponent(
419
+ Animated.Image,
420
+ { isScrollable: false }
421
+ );
419
422
  ```
420
423
 
421
- 2. Children are automatically measured
424
+ ---
422
425
 
423
- ```tsx
424
- <Transition.Pressable
425
- sharedBoundTag="card"
426
- onPress={() => router.push("/detail")}
427
- >
428
- {/* These children will be automatically measured when parent is pressed */}
429
- <Transition.View sharedBoundTag="title">
430
- <Text>Title</Text>
431
- </Transition.View>
432
- <Transition.View sharedBoundTag="subtitle">
433
- <Text>Subtitle</Text>
434
- </Transition.View>
435
- </Transition.Pressable>
436
- ```
426
+ ## Hooks
427
+
428
+ ### `useScreenAnimation`
437
429
 
438
- 3. Drive the animation with the object API
430
+ Access animation state within a screen component:
439
431
 
440
432
  ```tsx
441
- screenStyleInterpolator: ({ activeBoundId, bounds }) => {
442
- "worklet";
433
+ import { useScreenAnimation } from "react-native-screen-transitions";
443
434
 
444
- const styles = bounds({
445
- method: "transform", // "transform" | "size" | "content"
446
- space: "relative", // "relative" | "absolute"
447
- scaleMode: "match", // "match" | "none" | "uniform"
448
- anchor: "center", // see anchors below
449
- // target: "bound" | "fullscreen" | { x, y, width, height, pageX, pageY }
450
- // gestures: { x?: number; y?: number }
435
+ function DetailScreen() {
436
+ const animation = useScreenAnimation();
437
+
438
+ const style = useAnimatedStyle(() => {
439
+ const { current } = animation.value;
440
+ return {
441
+ opacity: current.progress,
442
+ };
451
443
  });
452
444
 
453
- return { [activeBoundId]: styles };
454
- };
445
+ return <Animated.View style={style}>...</Animated.View>;
446
+ }
455
447
  ```
456
448
 
457
- 3. Raw values when you need them
449
+ ---
458
450
 
459
- ```tsx
460
- const raw = bounds({ method: "transform", raw: true });
461
- // { translateX, translateY, scaleX, scaleY }
462
- ```
451
+ ## Animation Specs
463
452
 
464
- Or for size/content methods:
453
+ Configure spring/timing animations:
465
454
 
466
455
  ```tsx
467
- const toSize = bounds({
468
- method: "size",
469
- target: "fullscreen",
470
- space: "absolute",
471
- raw: true,
472
- });
473
- // { width, height, translateX, translateY }
474
-
475
- const content = bounds({ method: "content", raw: true });
476
- // { translateX, translateY, scale }
477
- ```
478
-
479
- Anchors and scale
480
-
481
- - `anchor`: "topLeading" | "top" | "topTrailing" | "leading" | "center" | "trailing" | "bottomLeading" | "bottom" | "bottomTrailing"
482
- - `scaleMode`: "match" | "none" | "uniform"
456
+ transitionSpec: {
457
+ open: {
458
+ stiffness: 1000,
459
+ damping: 500,
460
+ mass: 3,
461
+ overshootClamping: true,
462
+ },
463
+ close: {
464
+ stiffness: 1000,
465
+ damping: 500,
466
+ mass: 3,
467
+ overshootClamping: true,
468
+ },
469
+ }
483
470
 
484
- Targets and space
471
+ // Or use the default
472
+ transitionSpec: {
473
+ open: Transition.Specs.DefaultSpec,
474
+ close: Transition.Specs.DefaultSpec,
475
+ }
476
+ ```
485
477
 
486
- - `target`: "bound" (default), "fullscreen", or explicit `{ x, y, width, height, pageX, pageY }`
487
- - `space`: "relative" (within layout constraints) or "absolute" (window coordinates)
478
+ ---
488
479
 
489
- Gestures (sync focused screen deltas)
480
+ ## Masked View Setup
490
481
 
491
- - `gestures`: `{ x?: number; y?: number }` adds live drag offsets to the computed transforms
482
+ Required for `SharedIGImage` and `SharedAppleMusic` presets.
492
483
 
493
- Deprecated builder API
484
+ > **Note**: Requires native code. Will not work in Expo Go.
494
485
 
495
- - The old chainable builder (`bounds().relative().transform().build()`) is deprecated. Migrate to the object form shown above. The builder remains temporarily for backward compatibility.
486
+ ```bash
487
+ # Expo
488
+ npx expo install @react-native-masked-view/masked-view
496
489
 
497
- Quick access: `bounds.get()`
490
+ # Bare React Native
491
+ npm install @react-native-masked-view/masked-view
492
+ cd ios && pod install
493
+ ```
498
494
 
499
- Use `bounds.get(id?, phase?)` to retrieve raw measurements and the resolved style for any bound in a given phase (`current`, `next`, `previous`).
495
+ Wrap destination screen content:
500
496
 
501
497
  ```tsx
502
- const { bounds: metrics, styles } = bounds.get("hero", "current");
498
+ export default function DetailScreen() {
499
+ return (
500
+ <Transition.MaskedView style={{ flex: 1 }}>
501
+ {/* screen content */}
502
+ </Transition.MaskedView>
503
+ );
504
+ }
503
505
  ```
504
506
 
505
- ## Animating individual components with `styleId`
507
+ ---
508
+
509
+ ## Native Stack
510
+
511
+ For cases where you need native screen primitives, use the native stack integration. This extends `@react-navigation/native-stack` with custom transition support.
506
512
 
507
- Use `styleId` to animate a single view inside a screen.
513
+ > **Note**: The native stack has limitations. It uses `beforeRemove` listeners and transparent modals to intercept transitions. The Blank Stack is recommended for most use cases.
508
514
 
509
- 1. Tag the element:
515
+ ### Setup
510
516
 
511
517
  ```tsx
512
- <Transition.View
513
- styleId="fade-box"
514
- style={{ width: 100, height: 100, backgroundColor: "crimson" }}
518
+ import { createNativeStackNavigator } from "react-native-screen-transitions/native-stack";
519
+
520
+ const Stack = createNativeStackNavigator();
521
+
522
+ <Stack.Screen
523
+ name="Detail"
524
+ options={{
525
+ enableTransitions: true, // Required to enable custom transitions
526
+ ...Transition.Presets.SlideFromBottom(),
527
+ }}
515
528
  />
516
529
  ```
517
530
 
518
- 2. Drive it from the interpolator:
531
+ ### Expo Router Setup
519
532
 
520
533
  ```tsx
521
- screenStyleInterpolator: ({ progress }) => {
522
- "worklet";
534
+ import type {
535
+ ParamListBase,
536
+ StackNavigationState,
537
+ } from "@react-navigation/native";
538
+ import { withLayoutContext } from "expo-router";
539
+ import {
540
+ createNativeStackNavigator,
541
+ type NativeStackNavigationEventMap,
542
+ type NativeStackNavigationOptions,
543
+ } from "react-native-screen-transitions/native-stack";
523
544
 
524
- return {
525
- "fade-box": {
526
- opacity: interpolate(progress, [0, 1, 2], [0, 1, 0]),
527
- },
528
- };
529
- };
545
+ const { Navigator } = createNativeStackNavigator();
546
+
547
+ export const Stack = withLayoutContext<
548
+ NativeStackNavigationOptions,
549
+ typeof Navigator,
550
+ StackNavigationState<ParamListBase>,
551
+ NativeStackNavigationEventMap
552
+ >(Navigator);
530
553
  ```
531
554
 
532
- The red square fades in as the screen opens.
555
+ ### Native Stack Options
533
556
 
534
- ## Known Issues
557
+ All standard `@react-navigation/native-stack` options are available, plus:
535
558
 
536
- - **Delayed Touch Events** There’s a noticeable delay in touch events when the transition is finished. If this affects your app, please hold off on using this package until a fix is available.
559
+ | Option | Type | Description |
560
+ |--------|------|-------------|
561
+ | `enableTransitions` | `boolean` | Enable custom transitions (sets presentation to transparent modal) |
562
+ | `screenStyleInterpolator` | `ScreenStyleInterpolator` | Function that returns animated styles |
563
+ | `transitionSpec` | `TransitionSpec` | Animation config for open/close |
564
+ | `gestureEnabled` | `boolean` | Whether swipe-to-dismiss is allowed |
565
+ | `gestureDirection` | `GestureDirection \| GestureDirection[]` | Allowed swipe directions |
566
+ | `gestureVelocityImpact` | `number` | How much velocity affects dismissal |
567
+ | `gestureResponseDistance` | `number` | Distance threshold for gesture |
568
+ | `gestureDrivesProgress` | `boolean` | Whether gesture drives animation |
569
+ | `gestureActivationArea` | `GestureActivationArea` | Where gesture can start |
537
570
 
538
- ## Support and Development
571
+ ### Renamed Native Options
539
572
 
540
- This package is provided as-is and is developed in my free time. While I strive to maintain and improve it, please understand that:
573
+ To avoid collisions with custom gesture options, some native options are renamed:
574
+
575
+ | React Navigation | Renamed to |
576
+ |------------------|------------|
577
+ | `gestureDirection` | `nativeGestureDirection` |
578
+ | `gestureEnabled` | `nativeGestureEnabled` |
579
+ | `gestureResponseDistance` | `nativeGestureResponseDistance` |
541
580
 
542
- - **Updates and bug fixes** may take time to implement
543
- - **Feature requests** will be considered but may not be prioritized immediately
581
+ ### Limitations
582
+
583
+ - Overlay system not available
584
+ - Relies on `beforeRemove` listener to intercept navigation
585
+ - Uses transparent modal presentation
586
+ - Some edge cases with rapid navigation
587
+
588
+ ---
589
+
590
+ ## Known Issues
591
+
592
+ - **Delayed Touch Events** – There may be a noticeable delay in touch events when transitions finish. If this affects your app, consider using the Blank Stack.
593
+
594
+ ---
544
595
 
545
- I apologize for any inconvenience this may cause. If you encounter issues or have suggestions, please feel free to open an issue on the repository.
596
+ ## Support
546
597
 
547
- ### Support the project
598
+ This package is developed in my spare time. Updates and bug fixes may take time.
548
599
 
549
- I’ve estimated I downed around 60 cups of coffee while building this.
550
- If you’d like to fuel the next release, [buy me a coffee](https://buymeacoffee.com/trpfsu)
600
+ If you'd like to fuel the next release, [buy me a coffee](https://buymeacoffee.com/trpfsu)
551
601
 
552
602
  ## License
553
603