-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Body:
Describe the bug
When a Pressable or TouchableOpacity component is rendered within a React Navigation NavigationContainer, applying a conditional className via a template literal causes the app to crash with a "Couldn't find a navigation context" error.
This suggests that NativeWind's runtime processing of dynamic class names interferes with how React Context is passed down the component tree, specifically breaking the context provided by React Navigation. The issue does not occur if the className is a static string.
reproduction
https://stackblitz.com/edit/nativewind-test-veykuvuh?file=App.tsx,global.css
To Reproduce
- Set up a React Native project using
nativewind@^4.0.0and@react-navigation/native@^7.0.0. - Wrap the application's root component in a
<NavigationContainer>. - In any screen component rendered by the navigator, create a
Pressablecomponent whoseclassNameis determined by a state variable within a template literal.
Here is a minimal reproducible example:
import React, { useState } from 'react';
import { View, Text, Pressable, SafeAreaView } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { SafeAreaProvider } from 'react-native-safe-area-context';
const Stack = createNativeStackNavigator();
// This component will cause the crash
const MyFailingComponent = () => {
const [isActive, setIsActive] = useState(false);
return (
<Pressable
onPress={() => setIsActive(!isActive)}
// The dynamic className is the source of the problem
className={`p-4 rounded-lg ${isActive ? 'bg-blue-500' : 'bg-gray-500'}`}
>
<Text className="font-bold text-white">Toggle Me</Text>
</Pressable>
);
};
const HomeScreen = () => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ marginBottom: 20 }}>This screen will crash</Text>
<MyFailingComponent />
</View>
);
};
const App = () => {
return (
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
};
export default App;Expected behavior
The application should render without crashing. The Pressable component should correctly toggle its styles based on its state, and the React Navigation context should remain intact and available to all child components.
Screenshots
The app crashes with the standard React Native RedBox error:
"Render Error: Couldn't find a navigation context. Have you wrapped your app with 'NavigationContainer'?"
(You can drag-and-drop your screenshot here)
Environment:
- React Native: 0.80.0
- React: 19.1.0
- NativeWind: 4.1.23
@react-navigation/native: 7.1.14- OS: macOS
babel.config.js:module.exports = { presets: ['module:@react-native/babel-preset'], plugins: [ // ... other plugins 'nativewind/babel', ], };
Additional context and Workaround
We went through extensive debugging, and the only successful workaround is to completely remove the className prop and use StyleSheet.create instead.
This confirms the issue is with NativeWind's processing.
Workaround Code (This works):
import React, { useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';
const MyWorkingComponent = () => {
const [isActive, setIsActive] = useState(false);
return (
<Pressable
onPress={() => setIsActive(!isActive)}
style={[
styles.base,
isActive ? styles.activeButton : styles.inactiveButton,
]}
>
<Text style={styles.text}>Toggle Me</Text>
</Pressable>
);
};
const styles = StyleSheet.create({
base: {
padding: 16,
borderRadius: 8,
},
activeButton: {
backgroundColor: '#3b82f6', // bg-blue-500
},
inactiveButton: {
backgroundColor: '#6b7280', // bg-gray-500
},
text: {
fontWeight: 'bold',
color: 'white',
},
});
// ... The rest of the App code remains the same, just swap MyFailingComponent for MyWorkingComponentThis workaround strongly suggests that the Babel transform or runtime logic for NativeWind is altering the component in a way that makes it incompatible with React's Context API as used by React Navigation.