Ahmad Abdullah
Ahmad Abdullah

Reputation: 1761

React Navigation how to hide tabbar from inside stack navigation

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 is that can't pass any options to Tab navigation from Stack Navigation

Not all of the stack screens only one of them

Upvotes: 49

Views: 123953

Answers (29)

Ahmad Abdullah
Ahmad Abdullah

Reputation: 1761

To hide the tab bar in one of the screens, this works for React Navigation v4:

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

    let tabBarVisible = true;

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

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

    return {
        tabBarVisible,
    }
}

For v5, and v6 please check @Chathuranga Kasthuriarachchi's answer here

Upvotes: 67

Aliaksei Litsvin
Aliaksei Litsvin

Reputation: 1261

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

But probbaly you just need to consider to just redesign your routing tree. Maybe these screens just should be somewhere out of your tab navigator.

Upvotes: 2

Jorge Santos
Jorge Santos

Reputation: 46

In my case i had a TabBar(Parent) and inside it StackNavigator(Parent 2) and then my DrawerNavigator.

that is the solution that worked for me on react-navigation 6:

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

const Drawer = createDrawerNavigator();

const MyStack = ({ navigation, route }) => {    
  useFocusEffect(
      React.useCallback(() => {
        const parent = navigation.getParent();
        if (route.name === 'myRouteName')
          parent.setOptions({ tabBarStyle: { display: 'none' } });
        else parent.setOptions({ tabBarStyle: { display: 'flex' } });
        return () => {
          parent.setOptions({ tabBarStyle: { display: 'flex' } });
        };
      }, [navigation, route]),
    );

  return (
    <Drawer.Navigator>
        <Drawer.Screen name="myRouteName"/>
    </Drawer.Navigator>
  );
};

Upvotes: 0

OSMX
OSMX

Reputation: 45

just adding in screen options work for me

  tabBarButton: () => null,

Upvotes: 1

Bilal Abdeen
Bilal Abdeen

Reputation: 1947

For Rect Navigation v6.x, I found 3 different methods to hide the tabBar from some screens. The following script demonstrates each of these methods. Credit goes to the various answers for this question.

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

const ShiftUpdateEmployerStack = (params) => {
    // Method#1: Hides the tabBar on the screen(s), which satisfy a certain condition (or all screens), in a "stack"
    React.useLayoutEffect(() => {
        // (1.a) Hide the tabBar on all screens in this stack: 
        // params.navigation.setOptions({ tabBarStyle: {display: "none"} });

        // OR (1.b) Hide the tabBar on the screens, which satisfy a certain condition 
        const routeName = getFocusedRouteNameFromRoute(params.route);
        if (routeName === "EmployerMenuScreen") { // the screen, which you want to hide the tabBar on
            params.navigation.setOptions({ tabBarStyle: {display: "none"} });
        } else {
            params.navigation.setOptions({ tabBarStyle: {display: "flex"} });
        }
    }, [params.navigation, params.route]);
    
    const MyStack = createNativeStackNavigator();

    return (
        <MyStack.Navigator>
            <MyStack.Screen name="SiteListScreen" component={SiteListScreen}/>
        </MyStack.Navigator>
    );
};

// ///////// 
const MainNavigator = () => { 
    const MyNav = createBottomTabNavigator(); 

    return (
        <MyNav.Navigator 

            // (Optional) Using a custom bottom tab bar, instead of the default one. If a custom tab bar is used, additional code is required (below)
            tabBar={params => <CustomTabBarContent {...params} />}

            // Method#2: Hides the tabBar on some screen(s) 
            screenOptions={(params) => {
                return { 
                    tabBarStyle: { display: (params.route.name === "ListShiftEmployerScreen" ? "none" : "flex") }, 
                }; 
            }}
        >
            {/* Method#3: Hides the tabBar on all screens in the "stack" */}
            <MyNav.Screen name="InitialStack" component={InitialStack} options={{ tabBarLabel: "Home", tabBarStyle: {display: "none"}, }} />
        </MyNav.Navigator>
    );
}; 

// ///////// Using a custom tabBar
const CustomTabBarContent = ({ state, descriptors, navigation, }) => {
    // If the option (tabBarStyle: { display: "none" }) is used anywhere, the following couple of statements are required
    const focusedOptions = descriptors[state.routes[state.index].key].options;
    if (focusedOptions?.tabBarStyle?.display === "none") {
        return null;
    }

    return (
    <View style={{ flexDirection: "row" }}>
        {state.routes.map((route, index) => {
            // more code...
            // see https://reactnavigation.org/docs/bottom-tab-navigator 
        })}
    </View>
    );
}; 

Upvotes: 1

AmerllicA
AmerllicA

Reputation: 32472

Actually, by the docs explanation, it would be better to think deeply and re-arrange the screens to gather Tabbar screens underneath of TabStack and segregate the rest and especially the screens that you don't want to have Tabbar inside them, under the Navigation stack.

But if you want to hide the Tabbar in some sub-screens of a TabStack screen the following codes will be useful:

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

const Account = () => {
  const { getParent } = useNavigation();

  useEffect(() => {
    const parent = getParent();

    parent?.setOptions({
      tabBarStyle: { display: 'none' },
    });

    return () => {
      parent?.setOptions({
        tabBarStyle: { display: 'flex' },
      });
    };
  }, []);

  return (
    <View>
      ~~~

And I have created an useful custom hook for it:

import { useEffect } from 'react';
import useNavigation from './useNavigation';

const useHideTabBar = () => {
  const { getParent } = useNavigation();

  useEffect(() => {
    const parent = getParent();

    parent?.setOptions({
      tabBarStyle: { display: 'none' },
    });

    return () => {
      parent?.setOptions({
        tabBarStyle: { display: 'flex' },
      });
    };
  }, []);
};

export default useHideTabBar;

Upvotes: 1

Michel John
Michel John

Reputation: 21

<Navigator initialRouteName="Home"}>
    <Screen name="Home" component={Home} />
    <Screen
        name="About"
        component={About}
        options={{ tabBarStyle: { display: 'none' } }}
    />
</Navigator>

Upvotes: 2

praveen seela
praveen seela

Reputation: 518

export const useHideTabsHook = () => {
const route = useRoute();
const navigation = useNavigation();
const tabHiddenRoutes = getHiddenRoutesForRoute(route.name);
useLayoutEffect(() => {
    const routeName = getFocusedRouteNameFromRoute(route);
    if (tabHiddenRoutes && tabHiddenRoutes.includes(routeName)) {
        navigation.setOptions({ tabBarStyle: { display: 'none' } });
    } else {
        navigation.setOptions({ tabBarStyle: { display: 'flex' } });
    }
}, [navigation, route, tabHiddenRoutes]);
};

write your own logic to get. the routes in each tab getHiddenRoutesForRoute

const getHiddenRoutesForRoute = routeName => {
switch (routeName) {
    case 'tab1':
        return tab1HiddenRoutes;
    case 'tab2':
        return tab2HiddenRoutes;
    default:
        return null;
}
};

Include this hook in each tab stack. this is using React-Navigation v6.

Upvotes: 0

Oluwatobi Akanji
Oluwatobi Akanji

Reputation: 164

REACT Native V6

I am using typescript and I encountered such an issue too. Since the tabBarVisible option is no longer available, I used the tabBarStyle instead and set the display property to 'none'.

Suppose I have two screens, the Home screen which is the tabbed screen and another screen say Side screen which is a Stacked screen where I want to hide the tab bar, then I do this in the Stacked screen navigator component:

const setTabStyleVisibility = (shouldBeVisible: boolean) =>
shouldBeVisible
  ? ({
      tabBarStyle: { display: 'flex' },
    } as Partial<BottomTabNavigationOptions>)
  : ({
      tabBarStyle: { display: 'none' },
    } as Partial<BottomTabNavigationOptions>);

I have a custom button defined as:

<Button title={btnTitle} onPress={onPressHandler}/>

On the Home Screen, I have a button which navigates to the Side screen and hides the tab by defining the custom onPressedHandler prop to the custom button as:

onPressHandler={() => {
    navigation.setOptions({
      ...setTabStyleVisibility(false),
    });
    navigation.navigate("Side");
  }}

Then I had a button passed to the next screen (Side screen) where the tab bar will be shown on going back to Home screen. I set the onPressedHandler which is a custom prop to the custom button to

onPressHandler={() => {
    navigation.setOptions({
      ...setTabStyleVisibility(true),
    });
    navigation.navigate("Home");
  }}

Upvotes: 0

Ariel Batista
Ariel Batista

Reputation: 144

this worked for me:

<BottomTab.Navigator
  screenOptions={{
    tabBarStyle: {
      display: isTabVisible ? 'flex' : 'none'
    }
  }}>

where isTabVisible is a global state variable.

Upvotes: -2

Bilal Yaqoob
Bilal Yaqoob

Reputation: 998

Easy solution as mentioned in react-navigation library, You can change the navigation structure react-navigation-doc

Upvotes: 0

Miquel Ferrer
Miquel Ferrer

Reputation: 81

If, like me, you struggled to make this work in React Navigation 6, here is how I achieved it... I had a bottomTabNavigator containing several stacks, and wanted to hide the tabbar on a specific screen (player) of the homestack

    import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
    ...
    
    <BottomTabNavigator.Screen 
         name="HomeStack" 
         component={HomeStack} 
         options={({route}) =>({ tabBarStyle: getTabBarStyle(route) })} />
    
    ....
    const getTabBarStyle = (route) => {  
      const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
      let display = (routeName === 'Player') ? 'none':'flex';
      return {display}
    }

Upvotes: 7

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'}});
}

Upvotes: 75

Brock
Brock

Reputation: 583

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.

Upvotes: 13

FaysalB
FaysalB

Reputation: 398

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

Upvotes: 3

Udoka Okoye
Udoka Okoye

Reputation: 11

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])

Upvotes: 0

Paul Onteri
Paul Onteri

Reputation: 694

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;

Upvotes: 1

Mohammed alsheikh
Mohammed alsheikh

Reputation: 887

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;
}

};

Upvotes: 0

Hetmann W. Iohan
Hetmann W. Iohan

Reputation: 84

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 })

Upvotes: 1

user2210411
user2210411

Reputation: 1727

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}}>

Upvotes: 1

umer Hayyat
umer Hayyat

Reputation: 1

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

Upvotes: -1

Beatriz Lopes
Beatriz Lopes

Reputation: 11

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",
  },
})

Upvotes: 1

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
    }
},

})

Upvotes: 1

fannolo
fannolo

Reputation: 527

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,
});

Upvotes: 0

Marcelo Souza
Marcelo Souza

Reputation: 166

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} />,
  },
);

Upvotes: 0

Jordan Daniels
Jordan Daniels

Reputation: 5304

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

Upvotes: 31

OUBADI Ahmed
OUBADI Ahmed

Reputation: 162

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;

Upvotes: 8

With createBottomTabNavigator you can hide it with the defaultNavigationOptions

defaultNavigationOptions: {
  tabBarVisible: false,
},

Upvotes: 3

Pratap Sharma
Pratap Sharma

Reputation: 2743

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

Upvotes: 1

Related Questions