25
votes

I have the following stack navigation and screens:

export const HomeStack = createStackNavigator({
    Home: HomeScreen,
    Categories: CategoriesScreen,
    Products: ProductsScreen,
    ProductDetails: ProductDetailsScreen,
})

I want to hide tabs only in ProductDetailsScreen

export const hideTabBarComponents = [
    'ProductDetails',
]

export const MainTabs = createBottomTabNavigator(
    {
        Home: HomeStack,
        Favorite: FavoriteScreen,
        Account: AccountScreen,
        Help: HelpScreen,
        Events: EventsScreen
    },
    {
        navigationOptions: ({ navigation }) => ({

            tabBarIcon: ({ focused, tintColor }) => {
                ...
            },
            tabBarLabel: ({ focused, tintColor }) => {
                ...
            },

            tabBarVisible: ! hideTabBarComponents.includes(navigation.state.routeName)

        }),
    }
);

The problem that can't pass any options to Tab navigation from Stack Navigation

Not all of the stack screens only one of them

19

19 Answers

58
votes

The following code solved the problem:

HomeStack.navigationOptions = ({ navigation }) => {

    let tabBarVisible = true;

    let routeName = navigation.state.routes[navigation.state.index].routeName

    if ( routeName == 'ProductDetails' ) {
        tabBarVisible = false
    }

    return {
        tabBarVisible,
    }
}

This is an easy way, and it will work fine for both 4 and 5 versions

25
votes

For React Navigation 5, you can do this inside of the stack component:

props.navigation.dangerouslyGetParent().setOptions({
  tabBarVisible: false
});

https://reactnavigation.org/docs/en/navigation-prop.html#setoptions---update-screen-options-from-the-component

Be careful with using this though, you'll want to reset the tabBarVisible to true once unmounting the component.

For example, with React hooks inside the Stack component:

    useEffect(() => {
      const parent = props.navigation.dangerouslyGetParent();
      parent.setOptions({
        tabBarVisible: false
      });
      return () =>
        parent.setOptions({
          tabBarVisible: true
        });
    }, []);

Or you can reset the tabBarVisible in the Stack.Screen component with the back button press like this:

    const StackNav = (props) => (
      <Stack.Screen
        name='name'
        component={Name}
        options={{
          headerTitle: 'Name',
          headerLeft: () => (
            <Text
              onPress={() =>
                props.navigation.setOptions({
                tabBarVisible: true
                })
              }
            >
              on back
            </Text>
          )
        }}
      />
    }

(The second approach works better.)

22
votes

This is how I hide the tab bar in a specific screen in a stack (React Nav 5.x & 6.x)

import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
const ProfileStack = createStackNavigator();
    
const ProfileNavigator = ({ navigation, route }) => {
        React.useLayoutEffect(() => {
            const routeName = getFocusedRouteNameFromRoute(route);
            if (routeName === "Group"){
                navigation.setOptions({tabBarVisible: false});
            }else {
                navigation.setOptions({tabBarVisible: true});
            }
        }, [navigation, route]);
        return(
            <ProfileStack.Navigator>
                <ProfileStack.Screen name="Profile" component={ProfileScreen} />
                <ProfileStack.Screen name="Group" component={GroupScreen} />
            </ProfileStack.Navigator>
        )};

If you guys have number of screens that need to hide the tabbar use a string array of those route names and hide tabbar if focused route name includes in that array

const tabHiddenRoutes = ["Group","Map"];

if(tabHiddenRoutes.includes(getFocusedRouteNameFromRoute(route))){
  navigation.setOptions({tabBarVisible: false});
 }else{
 navigation.setOptions({tabBarVisible: true});
}

[Edit] - In case of v6, use display because tabBarVisible is deprecated in the favour of tabBarStyle-

if(tabHiddenRoutes.includes(getFocusedRouteNameFromRoute(route))){
  navigation.setOptions({tabBarStyle: {display: 'none'}});
 } else {
 navigation.setOptions({tabBarStyle: {display: 'flex'}});
}
8
votes

first let's creat a stack navigator and call it StackHome

const StackHome = createStackNavigator(
{
 Home: Home,
 CustomHide: CustomHide,
});
// This code let you hide the bottom app bar when "CustomHide" is rendering
StackHome.navigationOptions = ({ navigation }) => {
 let tabBarVisible;
  if (navigation.state.routes.length > 1) {
navigation.state.routes.map(route => {
  if (route.routeName === "CustomHide") {
    tabBarVisible = false;
  } else {
    tabBarVisible = true;
  }
});
 }

 return {
   tabBarVisible
 };
};
export default StackHome;
3
votes

With createBottomTabNavigator you can hide it with the defaultNavigationOptions

defaultNavigationOptions: {
  tabBarVisible: false,
},
2
votes

const AppNavigation = createBottomTabNavigator( {

    Learning:
    {
        screen: LearningStack,
        navigationOptions: {
            tabBarLabel: 'Learning',
            // tabBarVisible: false,
            tabBarIcon: ({ tintColor, focused }) => (
                // <Icon name='terminal' size={25} color={Colors.white} />
                <Image
                    source={require('../assets/TabBarIcon/Learning_96px.png')}
                    style={{ width: 45, height: '90%', }}
                />
            ),

        }
    },

Learning

Stack.navigationOptions

= ({ navigation }) => {

    let tabBarVisible = false;

    let routeName = navigation.state.routes[navigation.state.index].routeName

    if (routeName == 'Learning') {
        tabBarVisible = true
    }

    return {
        tabBarVisible
    }
},

})

1
votes

This is how I did. Select the stack in which you want to hide the tab bar. You can select it based on the index.

AppStack.navigationOptions = ({ navigation }) => {
  let tabBarVisible = true;
    if (navigation.state.index > 0) {
       tabBarVisible = false;
    }
    return {
       tabBarVisible
    };
};

Here is the link of the docs of React navigation

1
votes

You can also create a Modal inside the screen and make it always visible, with something like this:

const Example = () => {
  return(
    <ScrollView>
      <Modal animationType="slide" transparent={true} visible={true} >
          <View style={styles.modalExample}>
          </View>
      </Modal> 
    </ScrollView>
  )
}

And then style it:

const styles = StyleSheet.create({
  modalExample: {
    height:"100%",
    width:"100%",
    backgroundColor: "#000000",
  },
})
1
votes

Another way is to use the height of the tab bar to hide it. This is required when using createMaterialTopTabNavigator, since it does not have a tabBarVisible property. You can use a global state variable (Redux or UseState) or you can use something similar to the selected answer, where you keep track of the currently active screen.

Assuming 'hideTab' is a boolean.

const Tab = createMaterialTopTabNavigator();
...
<Tab.Navigator
   tabBarOptions={{style: {height: hideTab ? '0%' : null}}>
1
votes

I achieved that in this way. First the hook:

import {useLayoutEffect} from 'react';
import {useNavigation, useRoute, getFocusedRouteNameFromRoute} from '@react-navigation/native';

export function useHiddenTabs (hiddenTabRoutesArray, fallbackRoute) {
  const navigation = useNavigation();
  const route = useRoute();

  useLayoutEffect(() => {
    const routeName = getFocusedRouteNameFromRoute(route) ?? fallbackRoute;
    navigation.setOptions({
      tabBarVisible: !hiddenTabRoutesArray.includes(routeName),
    })
  }, [navigation, route]);
}

And then usage:

import {getFocusedRouteNameFromRoute} from '@react-navigation/native';

const routesWithoutTabs = ['ScreenWithoutTabs'];

const StackComponent = ({navigation}) => {

  useHiddenTabs(routesWithHiddenTabs, 'FallbackRoute');

  return (
    <Stack.Navigator /* other props */>
      <Stack.Screen
        name='ScreenWithoutTabs'
        component={ScreenWithoutTabsComponent}
      />
    </Stack.Navigator>
  );
};

Corresponding docs

1
votes

You can use the useFocusEffect hook to check if the screen is focused then hide the navigator with navigation.dangerouslyGetParent()

This is a custom hook I created:

import React from 'react';
import { useFocusEffect } from '@react-navigation/native';

const useHideTabBar = (navigation) => {

    useFocusEffect(
        React.useCallback(() => {

            // hide
            const parent = navigation.dangerouslyGetParent();
            parent.setOptions({
                tabBarVisible: false,
            });

            // reveal after changing screen
            return () =>
                parent.setOptions({
                    tabBarVisible: true,
                });

        }, []),
    );


};

export default useHideTabBar;
0
votes

This is the solution that I used in my project.

I have a bottom tab navigator, with 2 routes: Home and Profile. The ProfileHomePage route brings to a stack navigation ProfileStackNavigation.

Then, in the ProfileStackNavigation, I have the ProfileHomePage where the bottom tab should appears, and other child pages, where the bottom tabs should not be visible. I added a param tabBarVisible: false in that pages.

Finally, in the MainTabNavigator ProfileHomePage route, I added the navigationOptions function, to test if the current route has the param tabBarVisible.

const ProfileStackNavigation = createStackNavigator(
  {
    ProfileHomePage: ProfileHomePage,
    AboutAppPage: {screen: AboutAppPage, params: {tabBarVisible: false}},
    DiaryPage: {screen: DiaryPage, params: {tabBarVisible: false}},
    FilesPage: {screen: FilesPage, params: {tabBarVisible: false}},
    NotificationsPage: {screen: NotificationsPage, params: {tabBarVisible: false}},
  },
  {
    initialRouteName: 'ProfileHomePage',
  },
);

const MainTabNavigator = createBottomTabNavigator(
  {
    HomePage: HomePage,
    ProfileHomePage: {
      screen: ProfileStackNavigation,
      navigationOptions: ({ navigation }) => {
        const {params = {}} = navigation.state.routes[navigation.state.index];
        const tabBarVisible = params.tabBarVisible === false ? params.tabBarVisible : true;
        return {
          tabBarVisible,
        }
      }
    },
  },
  {
    initialRouteName: 'HomePage',
    tabBarComponent: props => <AppFooterTab {...props} />,
  },
);
0
votes

Another option here would be to add another stack navigator as a parent of the tab navigator, and put the screen that doesn't need the tab navigator there. This is the recommended way in versions of react navigation < 5

const FeedStack = createStackNavigator({
  FeedHome: FeedScreen,
  /* any other route you want to render under the tab bar */
});

const TabNavigator = createBottomTabNavigator({
  Feed: FeedStack,
  Profile: ProfileScreen,
});

const HomeStack = createStackNavigator({
  Tabs: TabNavigator,
  Details: DetailsScreen,
  /* any other route you want to render above the tab bar */
});

const AppNavigator = createSwitchNavigator({
  Auth: AuthScreen,
  Home: HomeStack,
});
0
votes

The are multiple ways to do it in v5: 1. use tabBarVisible option based on specific strings/route names

using the useRoute() hook & setOptions({ tabBarVisible: false })

2. use dangerouslyGetParent() method for dynamic control For specific screen

navigation.dangerouslyGetParent()?.setOptions({ tabBarVisible: false })

On scrolling hide the bottom tab bar

const navigation = useNavigation();

const handleScroll = ({ nativeEvent: { contentOffset: { y = 0 }} }) => {
  navigation.dangerouslyGetParent()?.setOptions({ tabBarVisible: y <= 50 })
}

return (
  <ScrollView onScroll={handleScroll} scrollEventThrottle={16}>
    ...
  </ScrollView>
)

3. for simple nesting you can pass the "tab bar navigation" to any stack screen props

// navigation -> is the bottomTab navigation instance
export default ({ navigation }) => {
  return (
    <Stack.Navigator screenOptions={screenOptions}>
      <Stack.Screen name="Trips">
        // add a new prop named tabNavigation={navigation}
        // you also have access to the stack navigation
        {(props) => <Trips tabNavigation={navigation} {...props} />}
      </Stack.Screen>
      <Stack.Screen name="Trip" component={Trip} />
    </Stack.Navigator>
  );
}

and use the examples from point 2

tabNavigation?.setOptions({ tabBarVisible: false })
0
votes

try this code and it's will work:

  const getTabBarVisibility = (route) => {
const routName = route.state
  ? route.state.routes[route.state.index].name
  : '';
if (routName == 'chat') {
  return false;
} else {
  return true;
}

};

0
votes
import React, {useLayoutEffect} from "react";
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';

const MainStackNavigator = ({navigation, route}) => {
    useLayoutEffect(() => {
        const routeName = getFocusedRouteNameFromRoute(route);
        if (routeName === 'Details') {
            navigation.setOptions({tabBarVisible: false});
        }else {
            navigation.setOptions({tabBarVisible: true});
        }
    }, [navigation, route])
0
votes

The flow goes like this

import {
  getFocusedRouteNameFromRoute,
} from "@react-navigation/native";
<BottomTab.Navigator
      screenOptions={(props) => {
        console.log(getFocusedRouteNameFromRoute(props.route));
        return {
          tabBarActiveTintColor: Colors.Red,
          tabBarInactiveTintColor: Colors.Blue,
          headerShown: false,
          tabBarStyle: {
            display:
              getFocusedRouteNameFromRoute(props.route) === "ProdDetails"
                ? "none"
                : "flex",
          },
        };
      }}
    >
{...children}
</BottomTab.Navigator>

Cheers

0
votes

if you are using navigation 6 : Than The tabBarVisible option is no longer present. You can achieve the same behavior by specifying tabBarStyle: { display: 'none' } in options.

-1
votes
const AppStack = createStackNavigator({
  SplashScreen: {
    screen: SplashScreen,
    navigationOptions: {
      header: null,
    },
  },
  Home: Home,
  CarouselMap: CarouselMap,
  HeatMapComponent: HeatMapComponent,
});