Skip to content

Bug: className with conditional expressions on Pressable breaks React Navigation context #269

@shenxiuqiang

Description

@shenxiuqiang

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

  1. Set up a React Native project using nativewind@^4.0.0 and @react-navigation/native@^7.0.0.
  2. Wrap the application's root component in a <NavigationContainer>.
  3. In any screen component rendered by the navigator, create a Pressable component whose className is 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 MyWorkingComponent

This 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions