36

I noticed that views in StackNavigation show the header title but if I set those same screens in a TabNavigation it doesn't show a header. It only shows a header if I wrap a StackNavigation either around each tab, or wrap the TabNavigation nested inside a StackNavigation.

Why don't screens in TabNavigation show a header - is that expected behavior? If so, is it better to have a StackNavigation in each tab, or one big StackNavigation around the TabNavigation?

// tabs navigation doesn't show a header title in each screen

const TabsNavigator = TabNavigator({
  Home: {
    screen:HomeScreen,
  },
  Profile: {
    screen: ProfileScreen,
  },
}, {
  tabBarOptions: {
    activeTintColor: '#e91e63',
  },
  navigationOptions: {
    header: {
      visible: true,
    },
  },
});

Header shows when I wrap it in a StackNavigator

default StackNavigator({
  Home: { screen: TabsNavigator },
});

Or is it better to do it this way

 export TabsNavigator = TabNavigator({
      Home: {
        screen:StackNavigator({
          Home: { screen: HomeScreen },
        }),
      },
      Profile: {
        screen: StackNavigator({Profile: {screen: ProfileScreen}}),
      },
    }, {
      tabBarOptions: {
        activeTintColor: '#e91e63',
      },
      navigationOptions: {
        header: {
          visible: true,
        },
      },
    });
MonkeyBonkey
  • 46,433
  • 78
  • 254
  • 460
  • It's expected behaviour. You'll indeed have to put a StackNavigator in each Tab if you want to have a header, or you'll have to create a header yourself. – ixje Aug 02 '17 at 07:53
  • i found solution here - https://stackoverflow.com/questions/53915174/header-title-is-empty-in-tabnavigation using that approach doesn't require to wrap with StackNavigator – Johnie May 27 '19 at 12:39

4 Answers4

17

Even if this is a fairly old question, I had myself this question a couple days ago, so I'll be adding my two cents about it hoping this will be useful for someone else in the future as well.

React Navigation is an amazing product with a high amount of customization, but that also turns out sometimes to be confusing to begin with, which also applies to some sections of the documentation. navigationOptions as of the current version states, is common for all screens but the "list of available navigation options depends on the navigator the screen is added to." https://reactnavigation.org/docs/tab-navigator.html#navigationoptions-used-by-tabnavigator hence the header option doesn't work because it's not available for TabNavigator itself.

Regarding your question on which approach is the best that depends on what do you want to accomplish with the navigation for your app itself. If you put your TabNavigator inside a StackNavigator the header component will be common for all of the tabs inside the TabNavigator, meaning the tab transition will take effect but the header won't move from its top position. If you on the other hand choose to nest a StackNavigator inside every tab, the header will render inside every tab, meaning the header will move along the tab transition animation.

I made a quick demo for you to see the difference, the code is also available if you wanna play with it.

Aaron Lu
  • 25
  • 5
  • Thank you for this answer! It seems clunk, but add another StackNavigator, but it works like a charm. – user1791914 Mar 10 '18 at 18:56
  • Thanks! I've implemented this, and works very well, but when I want to call a new screen (with StackNavigator), i'm getting this: `TypeError undefined is not an object (evaluating 'this.props.navigation.navigate')` I have this structure: `
    ` I'm using react-native-elements Header component, and setting `rightComponent` as a component class (in the same file, App.js), and in this component, in render method i do `const { navigate } = this.props.navigation;`, then in a button `onPress={ () => navigate('ContactoScreen') }`
    – Jeremias Araujo May 11 '18 at 16:52
  • I like how you explain – mr L Aug 24 '18 at 15:55
  • 2
    Thanks for the demo, but when I open the link it says "This project uses Expo SDK v22.0.0. If you want to open this project, the author will need to update the project's SDK version." – mukesh.kumar Aug 23 '19 at 13:33
2

I understand that this question already has an answer to it, but I would like to just show the way that I was able to make this work for myself using React Navigation > 5.x.x.

Juan's answer describes the best way to set this up, but also showing how it's done so that people can understand what the navigation looks like is also a bit of help for us visual learners. This was a little bit hard for me to understand at the time but hoping that it will help others in the future.

I find the best way to look at this is by breaking down the hierarchy of your apps routes. For me mine looked something like this.

               LoginNavigation                      HomeNavigation   
                     |                                    |
      LoginScreen        RegisterScreen        HomeStack      ProfileStack
                                                   |               |
                                              HomesScreen     ProfileScreen

My LoginNavigation stack contains two screens the Login screen and Register screen. In my app I don't need tab navigation set here so it's appropriate to just have these screens wrapped in a StackNavigator. On the other hand I have my HomeNavigation stack which contains a Home screen and a Profile screen. The only issue is that my screens are wrapped in a TabNavigator which does not produce a header.

To fix this we actually need to wrap our two screens (HomeScreen & ProfileScreen) in StackNavigators and then wrap everything with the TabNavigator. After realizing this, it actually makes much more sense to do it this way. Why? Well let's say that in my home screen I have some posts and I want the user to be able to see the post and all the comments that come with it. Well I wouldn't setup another Tab for this because that would just be silly. Instead we would add it to our HomeStack navigation instead like so.

                       HomeNavigation   
                             |
            HomeStack                 ProfileStack
                 |                         |
     HomesScreen    PostScreen       ProfileScreen

This structure now seems apparent when looking at something like the ProfileStack where there could be multiple branches to this stack, such as Settings, Followers, Pictures... Hopefully displaying the structure helps someone understand this as well as it helped me.

Below is my AppStackNavigator file to see the structure in code.

import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import Login from '../screens/Login';
import Register from '../screens/Register';
import FirstProfileImage from '../screens/FirstProfileImage';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import Home from '../screens/Home';
import Profile from './../screens/Profile';

const LoginStack = createStackNavigator();
const HomeTabs = createBottomTabNavigator();
const HomeStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const AppStack = createStackNavigator();

const LoginStackNavigator = () => (
  <LoginStack.Navigator screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}}>
    <LoginStack.Screen name="Login" component={Login}/>
    <LoginStack.Screen name="Sign Up" component={Register}/>
    <LoginStack.Screen name="Profile Image" component={FirstProfileImage}/>
  </LoginStack.Navigator>
)

const HomeStackNavigator = () => (
  <HomeStack.Navigator>
    <HomeStack.Screen name="Home" component={Home} />
  </HomeStack.Navigator>
)

const ProfileStackNavigator = () => (
  <ProfileStack.Navigator>
    <ProfileStack.Screen name="Profile" component={Profile} />
  </ProfileStack.Navigator>
)

const HomeTabNavigator = () => (
    <HomeTabs.Navigator>
      <HomeTabs.Screen name="HomeStack" component={HomeStackNavigator} />
      <HomeTabs.Screen name="ProfileStack" component={ProfileStackNavigator} />
    </HomeTabs.Navigator> 
)

const AppStackNavigator = () => (
    <AppStack.Navigator initialRouteName={'HomeScreen'} screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}} headerMode='none'>
      <AppStack.Screen name="LoginScreen" component={LoginStackNavigator}/>
      <AppStack.Screen name="HomeScreen" component={HomeTabNavigator}/>
    </AppStack.Navigator>  
)

export default AppStackNavigator;

I do agree that this is a bit overkill and that we could eliminate the issue of having to keep track of each individual navigator, but it's what works for now until they add a feature for this. Hopefully this explanation helps a bit. Thanks for coming to my TED talk.

Michael
  • 1,454
  • 3
  • 19
  • 45
0

According to React-Navigation TabNavigator Docs there is no header navigationOption. Therefore, when you write the following code you are actually setting a nonexisting value thus what you are doing does not affect anything.

navigationOptions: {
  header: { visible: true },
}

Sadly, you need a StackNavigator in this situation.

Tukan
  • 2,253
  • 15
  • 17
  • 1
    I'm not entirely sure if this is new, but there ***is*** a `header` property in `navigationOptions`, inside the `StackNavigator` documentation, but what he may have missed is the `navigationOptions` object it's not a global object with all properties in common, they depend on the navigator itself https://reactnavigation.org/docs/navigators/stack#header – Juan Carlos Alpizar Chinchilla Nov 05 '17 at 17:48
  • Thank you for letting me know. At the time I wrote this answer, I do not recall that existing, but it may have changed in the meantime. – Tukan Nov 05 '17 at 19:50
  • 1
    @JuanCarlosAlpizarChinchilla thanks for the source code! Nice and sweet! – shimatai Nov 21 '17 at 00:17
  • 1
    @shimatai yw! always glad to help whenever possible :) – Juan Carlos Alpizar Chinchilla Nov 21 '17 at 11:19
0

A general structure that I use is this, create a tab navigator and then render stacks for each tab. In this example I don't want a header on my LiveMap but I do on the other two tabs. The naming is up to you for whatever makes sense. Then inside each stack I render the screens that make sense.

const Tab = createBottomTabNavigator();
const TrainStack = createStackNavigator();
const MapStack = createStackNavigator();
const BusStack = createStackNavigator();

then each stack renders whatever screens I want for that subset

const MapNavigator = () => {
  return (
    <MapStack.Navigator
      initialRouteName="LiveMap"
      screenOptions={{headerShown: false}}>
      <MapStack.Screen name="Live Map" component={LiveMap} />
      <MapStack.Screen name="Show Routes" component={AllRoutes} />
      <MapStack.Screen name="Select Routes" component={SelectRoutes} />
    </MapStack.Navigator>
  );
};

and the tab navigator will render my stacks for each tab.

const MainNavigator = () => (
  <Tab.Navigator
    tabBarOptions={{
      activeTintColor: 'orange',
      keyboardHidesTabBar: true,
    }}
    initialRouteName="Live Map">
    <Tab.Screen
      name="Buses"
      component={BusNavigator}
      options={{
        tabBarLabel: 'Buses',
        tabBarIcon: ({color, size}) => (
          <BusIcon height={30} width={30} color={color} />
        ),
      }}
    />
    <Tab.Screen
      name="Live Map"
      component={MapNavigator}
      options={{
        tabBarLabel: 'Map',
        tabBarIcon: ({color}) => (
          <View
            height={100}
            width={100}
            style={{
              display: 'flex',
              alignItems: 'center',
              backgroundColor: '#fff',
              paddingTop: 8,
              borderRadius: 70,
            }}>
            <MapIcon height={50} width={50} color="orange" />
          </View>
        ),
      }}
    />
    <Tab.Screen
      name="Trains"
      component={TrainNavigator}
      options={{
        tabBarLabel: 'Trains',
        tabBarIcon: ({color, size}) => (
          <TrainIcon height={30} width={30} color={color} />
        ),
      }}
    />
  </Tab.Navigator>
Shane Walker
  • 301
  • 3
  • 7