MetaMask / MetaMask/metamask-mobile
Use native-stack for the heavy AppFlow/Main navigators instead of JS stack
- Dominant language
- TypeScript
- Stars
- 3k
- Forks
- 1.7k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
> **Performance audit finding** · Severity: **Medium** · Effort: Hard · Fix risk: Risky · Test safety net: Uncovered
> Owner: `@MetaMask/mobile-platform (suggested)`
> File: `app/components/Nav/App/App.tsx:3`
### What is this about?
The app's two largest navigators — the root `AppFlow` (`app/components/Nav/App/App.tsx`) and the post-login `MainNavigator` (`app/components/Nav/Main/MainNavigator.js`) — are built with `@react-navigation/stack` (the JavaScript-driven stack), not `@react-navigation/native-stack`. The native-stack package (already a dependency: `@react-navigation/native-stack: ^6.0.0`, see `package.json`) renders screens with the platform's native navigation primitives (`UINavigationController` / `Fragment`), running transitions and gesture handling off the JS thread, whereas the JS stack runs card transitions and the gesture/animation interpolators on the JS thread via Reanimated/Animated.
**Why it matters**
Every push/pop in these navigators (which carry ~70 and several dozen screens respectively) drives card-style transitions through JS-thread interpolators (`cardStyleInterpolator`), competing with controller state updates, list rendering, and websocket-driven re-renders that are common right after navigation. On lower-end Android devices this manifests as dropped frames during screen transitions. Native-stack offloads the transition/gesture work, freeing the JS thread during the most render-heavy moments.
### Scenario
N/A — see Technical Details.
### Design
N/A — internal performance change; no UI/design impact.
### Technical Details
**Evidence**
`app/components/Nav/App/App.tsx:3-6`:
```ts
import { createStackNavigator, type StackNavigationOptions } from '@react-navigation/stack';
// ...
const Stack = createStackNavigator();
```
`app/components/Nav/Main/MainNavigator.js:3` and `:161`:
```js
import { createStackNavigator } from '@react-navigation/stack';
// ...
const Stack = createStackNavigator();
```
Both navigators register a large number of `Stack.Screen`s and rely on JS `cardStyleInterpolator` transitions (e.g. `slideFromRightAnimation`, `fadeAnimation` in MainNavigator.js:171-208).
**Fix**
Migrate the heavy flows to `createNativeStackNavigator`. This is incremental: native-stack supports most screen options, and `presentation: 'modal'/'transparentModal'` map to native equivalents. Custom `cardStyleInterpolator` transitions must be replaced with native-stack `animation` options (`slide_from_right`, `fade`, etc.) or kept as JS stack only for the specific screens that genuinely need bespoke interpolators (e.g. the transparent account-selector overlay). Start with the leaf flows that have no custom interpolator, then evaluate the root.
### Threat Modeling Framework
N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.
### Acceptance Criteria
- 1. Build on a low/mid-tier Android device and record FPS (Perf Monitor / `react-native-performance`) during repeated push/pop on Settings and asset-detail screens before/after.
2. Confirm transition visuals and gesture-back still match design on both platforms.
3. Verify modal/transparent presentations (account selector, confirmation modals) still render correctly.
### References
- File: `app/components/Nav/App/App.tsx:3`
- Source: MetaMask Mobile performance audit — finding `nav-appflow-js-stack-vs-native-stack`
- Owner (CODEOWNERS / best-effort): @MetaMask/mobile-platform (suggested)
- Status: **UNVALIDATED**
Contributor guide
Research direction
Start with app/components/Nav/App/App.tsx and app/components/Nav/Main/MainNavigator.js, then review the native-stack dependency in package.json and the existing transition options. Establish baseline and comparison builds on a low/mid-tier Android device using repeated Settings and asset-detail push/pop; done means matching transitions and back gestures on both platforms, with modal and transparent presentations still rendering correctly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, react-native, typescript
- Domain
- frontend, mobile, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100