Reusing Stack with Group when nesting Navigation

Using React Navigation 6, in the documentation, they recommend you use Groups to minimize nested navigators.

However I’m not sure how to do that in this example, using a nested Stack in a Tab Navigator:

import * as React from 'react';
import { View, Button } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

function FeedScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button
        title="Go to Settings"
        onPress={() => navigation.navigate('Settings')}
      />
    </View>
  );
}

function ProfileScreen() {
  return <View />;
}

function SettingsScreen() {
  return <View />;
}

const FeedStack = createNativeStackNavigator();

function FeedStackScreen() {
  return (
    <FeedStack.Navigator>
      <FeedStack.Screen name="Feed" component={FeedScreen} />
      {/* other screens */}
    </FeedStack.Navigator>
  );
}

const ProfileStack = createNativeStackNavigator();

function ProfileStackScreen() {
  return (
    <ProfileStack.Navigator>
      <ProfileStack.Screen name="Profile" component={ProfileScreen} />
      {/* other screens */}
    </ProfileStack.Navigator>
  );
}

const Tab = createBottomTabNavigator();

function HomeTabs() {
  return (
    <Tab.Navigator screenOptions={{headerShown: false}}>
      <Tab.Screen name="Feed" component={FeedStackScreen} />
      <Tab.Screen name="Profile" component={ProfileStackScreen} />
    </Tab.Navigator>
  );
}

const RootStack = createNativeStackNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <RootStack.Navigator>
        <RootStack.Screen
          name="Home"
          component={HomeTabs}
          options={{ headerShown: false }}
        />
        <RootStack.Screen name="Settings" component={SettingsScreen} />
      </RootStack.Navigator>
    </NavigationContainer>
  );
}

When I reuse the Stack so only one is used in the Tab Navigator it still works:

const TabStack = createNativeStackNavigator();

function FeedStackScreen() {
  return (
    <TabStack.Navigator>
      <TabStack.Screen name="Feed" component={FeedScreen} />
      {/* other screens */}
    </TabStack.Navigator>
  );
}

function ProfileStackScreen() {
  return (
    <TabStack.Navigator>
      <TabStack.Screen name="Profile" component={ProfileScreen} />
      {/* other screens */}
    </TabStack.Navigator>
  );
}

const Tab = createBottomTabNavigator();

function HomeTabs() {
  return (
    <Tab.Navigator screenOptions={{headerShown: false}}>
      <Tab.Screen name="Feed" component={FeedStackScreen} />
      <Tab.Screen name="Profile" component={ProfileStackScreen} />
    </Tab.Navigator>
  );
}

However when I try to use Group like this:

const TabStack = createNativeStackNavigator();

function FeedStackScreen() {
  return (
    <TabStack.Group>
      <TabStack.Screen name="Feed" component={FeedScreen} />
      {/* other screens */}
    </TabStack.Group>
  );
}

function ProfileStackScreen() {
  return (
    <TabStack.Group>
      <TabStack.Screen name="Profile" component={ProfileScreen} />
      {/* other screens */}
    </TabStack.Group>
  );
}

const Tab = createBottomTabNavigator();

function HomeTabs() {
  return (
    <Tab.Navigator screenOptions={{headerShown: false}}>
      <TabStack.Navigator>
        <Tab.Screen name="Feed" component={FeedStackScreen} />
        <Tab.Screen name="Profile" component={ProfileStackScreen} />
      </TabStack.Navigator>
    </Tab.Navigator>
  );
}

I get the error a navigator can only contain 'screen' 'group' or 'react.fragment' because you can’t have a navigator inside another without writing it in the component.

Is there a way to use Group here or is the correct way to write <TabStack.Navigator></TabStack.Navigator> for each of the "Groups"?

>Solution :

The problem here is that Group is a component returned by the navigator. If we create a JSX component that returns that group instead, then this is technically not a Group anymore but its own JSX component.

This is an unsolved problem in react-native-navigation and is discussed in this GitHub issue, but has apparently no satisfying solution other than an ugly hack.

We can call the functional component inline.

function CustomGroupComponent() {
  return <Tab.Group>
          <Tab.Screen ... />
          <Tab.Screen ... />
      </Tab.Group>
}

export const Tabs = (props) => {

  return (

     <Tab.Navigator ...>
         <Tab.Screen ...>
         {CustomGroupComponent()}
     </Tab.Navigator>
  )
}

Thus in summary, there is no pretty solution. The trick is to call the functional component as a function inline using {CustomGroupComponet()}.

Remarks: I would consider this as an anti-pattern, but there is no other solution in the current version as far as I am concerned.

Leave a Reply