5

I have a Stack Navigator with some screens and an initial route like "Profile", and when I navigate to "Options" via a navigation.navigate("Options") from the "Profile" screen, I don't want to see the bottom tabs. Here's an example of what I have:

ProfileNav.js

export default function ProfileNav () {
   return (
      <Stack.Navigator initialRoutName="Profile">
         <Stack.Screen name="Profile" component={ProfileScreen}>
         <Stack.Screen name="Options" component={OptionsScreen}>
      </Stack.Navigator>
   );
};

TabNav.js

export default function TabNav () {
   return (
      <Tab.Navigator initialRouteName="Home">
         <Tab.Screen name="Home" component={HomeScreen}>
         <Tab.Screen name="ProfileNav" component={ProfileNav}>
      </Tab.Navigator>
   );
};

I'm using React Navigation v6. I've seen the Hiding tab bar in specific screens docs describe how to swap around screens to achieve hiding the tabs from a single screen, but in this case I'm trying to have the parent screen of the ProfileNav stack still show the bottom tabs, but I don't want the rest of the screens in the stack to show them, which is not what the docs help in achieving unless I missed something.

So how do I achieve hiding the bottom tabs from select screens of a stack navigator nested in a tab navigator?

I have also tried passing in tabBarVisible into the "Options" screen options, but this didn't work.

2

4 Answers 4

11

You can pass the navigationContainerRef to the NavigationContainer and get the current route name via getCurrentRoute in the TabNav component in order to hide the tab bar for specific screens that are handled by a different navigator.

Then, pass the route name as a state to the Tab navigator and decide for which screens to show the tab bar and for which not.

In this case, you want to show the TabBar for the route Profile only. Here is a minimal working example.

import React, {useState} from 'react';
import { Text, View, StyleSheet, Button } from 'react-native';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {NavigationContainer} from '@react-navigation/native';
import { createNavigationContainerRef } from "@react-navigation/native"

const Stack = createNativeStackNavigator()

function ProfileNav () {
   return (
      <Stack.Navigator initialRoutName="Profile">
         <Stack.Screen name="Profile" component={ProfileScreen} />
         <Stack.Screen name="Options" component={OptionsScreen} />
      </Stack.Navigator>
   );
}

const Tab = createBottomTabNavigator();

function TabNav (props) {
  const hide = props.routeName != "Profile"
   return (
      <Tab.Navigator initialRouteName="Home">
         <Tab.Screen name="Home" component={HomeScreen} />
         <Tab.Screen name="ProfileNav" component={ProfileNav} options={{
          headerShown: false,
          tabBarStyle: { display: hide ? "none" : "flex" }
        }} />
      </Tab.Navigator>
   );
}

function HomeScreen() {
  return <View></View>
}

function ProfileScreen(props) {
  return <View>
     <Button title="Nav to options" onPress={() => props.navigation.navigate("Options")}></Button>
  </View>
}

function OptionsScreen() {
  return <View></View>
}

const ref = createNavigationContainerRef();

export default function App() {
  const [routeName, setRouteName] = useState();

  return (
    <NavigationContainer
      ref={ref}
      onReady={() => {
        setRouteName(ref.getCurrentRoute().name)
      }}
      onStateChange={async () => {
        const previousRouteName = routeName;
        const currentRouteName = ref.getCurrentRoute().name;
        setRouteName(currentRouteName);
      }}
    >
       <TabNav routeName={routeName} />
     </NavigationContainer>
  );
}

And here is a working snack.

5
  • 2
    Your answer works, my app is a bit more complicated than the example but I'll try out this concept
    – patataskr
    Commented Mar 24, 2022 at 19:01
  • It should work nevertheless. Let me know if it doesn't and provide me with more details of your application, then I am sure we can fix open problems. Commented Mar 24, 2022 at 19:04
  • I was able to implement your logic except I was getting some weird behavior with props.routeName I'm assuming bc of prop drilling (my nav tree is large). I decided to create a context which returns the routeName, so where you have setRouteName in NavigationContainer I have handleRoute, which is imported from my context. Works like a charm, thanks!
    – patataskr
    Commented Mar 25, 2022 at 16:14
  • Perfect answer! Commented Sep 20, 2022 at 20:30
  • Your code and the snack code are not the same. Commented Dec 22, 2022 at 20:15
0

You can follow the react navigation implementation just keep outside whichever stack screen that should not be part of bottom navigation as a stack screen in app component like below.

export default function ProfileNav () {
return (
  <Stack.Navigator initialRoutName="Profile">
     <Stack.Screen name="Profile" component={ProfileScreen}>
  </Stack.Navigator>
   );
};

function HomeTabs() {
   return (
    <Tab.Navigator>
    <Tab.Screen name="Home" component={Home} />
    <Tab.Screen name="ProfileNav" component={ProfileNav} />
    </Tab.Navigator>
  );
 }

 function App() {
    return (
     <NavigationContainer>
       <Stack.Navigator>
       <Stack.Screen name="HomeTabs" component={HomeTabs} />
       <Stack.Screen name="Options" component={OptionsScreen}>
       </Stack.Navigator>
   </NavigationContainer>
    );
 }
-1

you can use react's useLayoutEffect with getFocusedRouteNameFromRoute function from @react-navigation/native

first, we need to extract navigation and route from tab navigator params

then using useLayoutEffect every time the focused layout is changed we are checking for focused route name if the focused route name matches our route name ( in your case "Options" ) where are going to set "tabBarVisible" to "false"

export default function ProfileNav ({ navigation, route }) {
    React.useLayoutEffect(() => {
        const routeName = getFocusedRouteNameFromRoute(route);
        if (routeName == "Options") {
        navigation.setOptions({ tabBarVisible: false });
        } else {
        navigation.setOptions({ tabBarVisible: true });
        }
    }, [navigation, route]);
   return (
      <Stack.Navigator initialRoutName="Profile">
         <Stack.Screen name="Profile" component={ProfileScreen}>
         <Stack.Screen name="Options" component={OptionsScreen}>
      </Stack.Navigator>
   );
};
1
  • Still seeing tab bar on OptionsScreen
    – patataskr
    Commented Mar 24, 2022 at 18:47
-1

You need to call TabNavigator stack from StackNavigator instead, that should works.

export default function TabNav () {
   return (
      <Tab.Navigator>
         <Tab.Screen name="Home" component={HomeScreen}>
         <Tab.Screen name="Profile" component={ProfileScreen}>
      </Tab.Navigator>
   );
};

return(<NavigationContainer
>
    <Stack.Navigator initialRoutName="Home">
      <Stack.Screen name="Home" component={TabNav} />
      <Stack.Screen name="Options" component={OptionsScreen} />
    </Stack.Navigator>

 </NavigationContainer>)
2
  • Can't navigate to Options from Profile with this
    – patataskr
    Commented Mar 24, 2022 at 18:46
  • @patataskr Edited the answer. I tested that and I was able to navigate from profile to options. Make sure that you properly set your NavigationContainer and Stack. If TabNav called from Stack <Stack.Screen name="Home" component={TabNav} /> then you should be able to navigate to Options from Profile.
    – electroid
    Commented Mar 24, 2022 at 19:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.