software-mansion / software-mansion/react-native-screens

[iOS] App freezes with 100% CPU usage when a touchable is used inside a nested stack's `headerLeft` (repro provided + reproduces consistently)

Open
#3,939 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

platform:ios repro-provided
Dominant language
TypeScript
Stars
3.7k
Forks
714
Avg merge
2d 23h
Merged PRs (30d)
71

Description

Description

When the app has a specific combination of bottom tabs, nested stack navigators and headerLeft, it can freeze and stop responding to input. The Xcode debugger shows 100% CPU usage while the app is frozen.

This only reproduces on iOS < 26. I've verified the issue on simulators with iOS 18.6 and 17.5.

https://github.com/user-attachments/assets/ceceec01-0244-46e8-b2f9-427742ebb390

Steps to reproduce
  1. Run the code below. ⚠️ Important: run on iOS < 26 to reproduce the bug.
  2. Press "Navigate to Form screen", then "Navigate to Detail screen", then "Go back".
  3. Repeat step 2 a few times for good measure. Often doing it once is enough.
  4. Press on "TabB".
  5. Observe that the app no longer responds to touches. Observe high CPU usage and an eventual crash.
import { useLayoutEffect } from 'react';
import { Button, ScrollView, Text, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const MainScreen = ({ navigation }) => (
  <Button
    title="Navigate to Form screen"
    onPress={() => navigation.navigate('Form')}
  />
);

const ScrollInnerScreen = () => (
  <ScrollView>
    {Array.from({ length: 50 }, (_, i) => (
      <Text key={i}>{i}</Text>
    ))}
  </ScrollView>
);

const nestedStack = createNativeStackNavigator();

const ScrollScreen = () => (
  <nestedStack.Navigator>
    <nestedStack.Screen name="ScrollContent" component={ScrollInnerScreen} />
  </nestedStack.Navigator>
);

const bottomTabs = createBottomTabNavigator();

const BottomTabsScreen = () => (
  <bottomTabs.Navigator initialRouteName="TabA">
    <bottomTabs.Screen name="TabA" component={MainScreen} />
    <bottomTabs.Screen name="TabB" component={ScrollScreen} />
  </bottomTabs.Navigator>
);

const DetailOverviewScreen = ({ navigation }) => {
  // Does not reproduce if I remove this
  useLayoutEffect(() => {
    navigation.setOptions({ headerLeft: () => <TouchableOpacity /> });
  }, [navigation]);

  return <Button title="Go back" onPress={() => navigation.goBack()} />;
};

const detailStack = createNativeStackNavigator();

const DetailScreen = () => (
  <detailStack.Navigator>
    <detailStack.Screen
      name="DetailOverview"
      component={DetailOverviewScreen}
    />
  </detailStack.Navigator>
);

const FormScreen = ({ navigation }) => (
  <Button
    title="Navigate to Detail screen"
    onPress={() => navigation.replace('Detail')}
  />
);

const rootStack = createNativeStackNavigator();

const App = () => (
  <NavigationContainer>
    <rootStack.Navigator>
      <rootStack.Screen name="Main" component={BottomTabsScreen} />
      <rootStack.Screen name="Detail" component={DetailScreen} />
      <rootStack.Screen name="Form" component={FormScreen} />
    </rootStack.Navigator>
  </NavigationContainer>
);

export default App;
  "dependencies": {
    "@react-navigation/bottom-tabs": "7.15.10",
    "@react-navigation/native": "7.2.2",
    "@react-navigation/native-stack": "7.14.12",
    "react": "19.2.3",
    "react-native": "0.85.2",
    "react-native-safe-area-context": "5.7.0",
    "react-native-screens": "4.24.0"
  }

I couldn't manage to reduce the repro further (e.g. remove bottom tabs) — seems that everything in the above code contributes to the freeze in some way.

Snack or a link to a repository

https://github.com/SimpleCreations/SEE_REPRO_ABOVE_sorry_bot

Screens version

4.24.0

React Native version

0.85.2

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Fabric (New Architecture)

Build type

Debug mode

Device

iOS simulator

Device model

iOS 18.6 simulator

Acknowledgements

Yes

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided reproduction or the linked repository on an iOS simulator below version 26, then follow the navigation sequence through Form, Detail, and TabB. Investigate the interaction between the nested stacks, bottom tabs, and the headerLeft TouchableOpacity set in DetailOverviewScreen. Done means the app remains responsive without sustained 100% CPU usage after repeating the sequence.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react-native
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.