react-navigation / react-navigation

Routing and navigation for your React Native apps

Home Page:https://reactnavigation.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to customize the back button in Stack Navigator

wangzhe890703 opened this issue · comments

I want to customize the back button in stack navigator. For example. I want to use an icon to replace the current back button. Does anyone know how to do that?

header: ({ goBack }) => ({
    left: ( <Icon name={'chevron-left'} onPress={ () => { goBack() } }  /> ),  
}),

Will it be hidden when there is no history just like native one?

I've tried, and it always show the back button even there is no history. Is there anyway to only customize the icon without to create the custom back button component ?

@sigmazen I tried doing it like that and I'm getting Objects are not valid as a React child (found: object with keys ({left}).
The docs say:

header
React Element or a function that given HeaderProps returns a React Element, to display as a header. Setting to null hides header.
so sounds like we're expected to return a React element and not an object?

my code:

const Left = ({ onPress }) => (
  <TouchableHighlight onPress={onPress}>
    <Image
      source={require('./Assets/BackLight.png')}
    />
  </TouchableHighlight>
);

const stackNavigatorConfigs = {
  initialRouteName: 'Dashboard',
  navigationOptions: {
    header: ({ goBack }) => ({
      left: <Left onPress={goBack} />,
    }),
  },
};

Thanks!

"react-navigation": "^1.0.0-beta.11",
"react-native": "0.43.0-rc.2",
"react": "16.0.0-alpha.4",

(repost from #777, sorry for the spam!)

@stantoncbradley It seems like customizing the header changed in the last few updates of React Native. Refering to the doc Stack Navigator, now you got to use 'headerLeft' to set an element on the left of the header.

Passing navigation options changed too. According to the doc Screen Navigation Options, Dynamic configuration is possible and the 'navigationOptions' now can be used as a function which take navigation Options as parameters.

For example, I wanted a button to close and open my DrawerNavigator in the header. In my router.jsfile, I wrote:

export const Root = StackNavigator({
    Login: {
        screen: LoginContainer,
        navigationOptions: {
            header: false,
        }
    },
    App: {
        screen: AppDrawer,
        navigationOptions: ({navigation}) => ({
            headerLeft: <BurgerMenu nav = {navigation} />,
        })
    }
}, {
    headerMode: 'screen',
});

In my BurgerMenu.jscomponent, I use navigation as a prop and call navigation.navigate:

_handleDrawer = () => {
        this.state.isOpen ? this.props.nav.navigate('DrawerClose') : this.props.nav.navigate('DrawerOpen');
        this.setState({isOpen: !this.state.isOpen});
    }

    render() {
        return (
            <TouchableOpacity
                onPress = {this._handleDrawer} >
                <Image style={styles.image} source={{uri: 'https://image.flaticon.com/icons/png/128/52/52045.png'}} />
            </TouchableOpacity>
        );
    }

In your case, instead of calling this.props.nav.navigate(), just call this.props.nav.goBack().

Hope it helps !

@FreddyPoly thanks so much! just saw the headerLeft syntax somewhere else while looking for something else haha! So I have that working now. Good to know about the navigationOptions!

commented

@prontiol @hoangnm Have you found the way to hide headerLeft on the first screen?

@tom29: If you're using Redux for state, then you get get the nav.index variable. If it is 0, then you're at the first page and no need to show 'Back' button.

I am using Redux with my react navigation and my screens are written in a routes.js file that looks something like:

import screenA from '../screens/screenA';
import screenB from '../screens/screenB';
import screenC from '../screens/screenC';

const Routes = {
  Main: {
    screen: screenA,
    navigationOptions: {
        title: 'screenA',
    },
  },
  Second: {
    screen: screenB,
    navigationOptions: {
        title: 'screenB',
    },
  },
  Third: {
    screen: screenC,
    navigationOptions: {
        title: 'screenC',
    },
  },
};

export default Routes;

Routes is used in another file and the navigation works fine.
I want to add a header button, I have a component called 'HeaderButton' that I want to add to screenA and screenB.

my versions:
react: 16.0.0-alpha.12
react-native: 0.45.1
react-navigation: 1.0.0-beta.11

how can I do that?
Routes is not a JSX scope, any thoughts so I can't do something like:

import HeaderButton from '../components/HeaderButton';
....
Main: {
    screen: screenA,
    navigationOptions: {
        title: 'screenA',
        headerRight: <HeaderButton />,
    },
},
commented

Using a snippet of code like this also gave the ability to navigate between screens

static navigationOptions = ({ navigation, screenProps }) => ({
    title:  'Header Title',
    headerLeft: <Icon name={'arrow-left'}
                            onPress={ () => { navigation.goBack() }} />,
    headerRight: <Icon name={'cog'}
                            onPress={ () => { navigation.navigate('Settings') }} />,
  });

How can i modify the status bar?

Sorry @glendenning , i got error cant find variable: Icon here,
i must import what ?

thannks

Hi, if you only need to change back button color, please take a look at this post #1104 (comment)

@MariusMeiners

Hai thanks.. Sorry for late answer i just impront icon from ionicons vektor thanks :)

commented

snippet by @glendenning is always rendering back button even there is no history in stack.
does anyone know some way to customize only icon. like headerBackImage does, but with ability to set component?

Render <MyCustomIcon /> everytime:

{
  navigationOptions: ({navigation}) => ({
    headerLeft: <MyCustomIcon />,
  })
}

Render <MyCustomIcon /> only if there is history (default behavior if not overridden)

{
  navigationOptions: ({navigation}) => ({
    headerLeft: () => <MyCustomIcon />,
  })
}

(react-navigation ^1.5.11)

@wemakeweb what's inside of <MyCustomIcon /> ?

here's mine :

render() {
        return (
            <TouchableOpacity 
                    style={{paddingRight:15, marginLeft:5}}
                    onPress={ ????? }
                >
                    <Image 
                        source={require('../../img/backLeft.png')}
                        style={{width:20, height:20, overlayColor:'#000'}}
                    />
                </TouchableOpacity>
        );
 }

(react-navigation ^1.5.11)

@JayKurniawan <MyCustomIcon /> is what ever you want to render as headerLeft.

Is there a way to render a custom element instead of the blank space? I want a custom watermark to appear when there is no "go back" state and the regular back button when there is.

If I just render an element as headerLeft is there anyway I can see if a back button is needed?

what about

navigationOptions: ({ navigation }) => {
...
headerLeft: navigation.state.routeName === 'WatermarkPage' ? <YourWatermark /> : <BackButton navigation={navigation} />
...
}

Kind of like that, but that code will toggle is based on the routeName, not on if the back button will be displayed or not. What I want is something like:

headerLeft: navigation.state.INDEX_NOT_0 ? <DefaultBackButton /> : <YourWatermark />

But it seems like there's no way to know (from navigation) if the route index is 0 (that is, if there's going to be a back button or not). Also, I'm not sure there's even DefaultBackButton either - it just magically appears when you navigate to a child page!

It seems to me the only way to do this is to track the navigation manually and maintain your own stack.

To custom icon of the back button from stack navigator use:

const createNavigation = createStackNavigator({
            First: {
            screen: FirstComponent,
            navigationOptions: ({ navigation}) => ({
                headerLeft: (
                    <TouchableOpacity onPress={() => navigation.goBack()}>
                        <Image source={ require('../image/source.png') }/>
                    </TouchableOpacity>
                )
            })
        
        }
    
})

That's the same solution as FreddyPoly above isn't it (perhaps you're just answering the original question)? I'd like a way to display a logo when there is no back button, and the regular back button when there is - but I can't see a way to tell if there should be a back button or not from the navigation parameters.

@brentvatne - dangerouslyGetParent sounds dangerous, but works for me! Thanks very much for that, I'd just about given up on this one... that's awesome!

If you want to customize the back button image, you can use headerBackImage in navigationOptions of a StackNavigator, which is used to replace the default back button image and it won't be displayed without history.

Render <MyCustomIcon /> everytime:

{
  navigationOptions: ({navigation}) => ({
    headerLeft: <MyCustomIcon />,
  })
}

Render <MyCustomIcon /> only if there is history (default behavior if not overridden)

{
  navigationOptions: ({navigation}) => ({
    headerLeft: () => <MyCustomIcon />,
  })
}

(react-navigation ^1.5.11)

Sorry to revive an old thread but do you know how to accomplish this when your header left contains multiple items? For example, I want to only show the back button when there is history, but always want to show the menu button.

headerLeft: () => (
        <View>
          <Feather name="arrow-left" size={42} onPress={() => navigation.goBack()} />
          <Feather name="menu" size={42} onPress={navigation.openDrawer} />
        </View>
      ),
commented

Add this code to have custom image on navigation bar back button.

Note : Change the image path according to your project.

static navigationOptions = ({ navigation }) => ({
        headerLeft : (
            <TouchableOpacity onPress={() => { navigation.goBack() }}>
                <View style={{ justifyContent: 'center', headerLayoutPreset: 'center', marginLeft: 15, width: 40, height: 40 }}>
                      <Image source={ require('../image/navBack.png') }/>
                </View>
            </TouchableOpacity>
        ),
    });
header: ({ goBack }) => ({
    left: ( <Icon name={'chevron-left'} onPress={ () => { goBack() } }  /> ),  
}),

Where should i apply this piece of code?

headerLeft not header: left

` static navigationOptions = ({ navigation }) => {

const back = (<TouchableWithoutFeedback onPress={() => navigation.goBack()}>
    <View style={{ marginLeft: 10, width: 50 }}>
        <AntDesign name="arrowleft" size={20} color={MyColors.white} />
    </View>
</TouchableWithoutFeedback>);
return { 
    headerLeft: back,
    title: "title",
    headerStyle: BasicStyle.headerStyle,
    headerTitleStyle: { fontWeight: "bold",color:MyColors.white },
};

};
`

this way you can customise back button
note : not tested in android

onPress={goBack}

header:({ navigation }) => (

                    <TouchableOpacity
                        style={{ alignItems: 'center', flexDirection: 'row' }}
                        onPress={()=>navigation.goBack()}
                    >

                        
                    </TouchableOpacity>
                   
        )

React Navigation 5
This will replace the back button everywhere it would normally appear (rather than place a button on the left that will appear always, even when there's no history in the stack, like when using headerLeft).

<Stack.Navigator
    screenOptions={{
        headerBackTitleVisible: false,
        headerBackImage: ()=>(<Text>testing....</Text>),
    }}
>

React Navigation 5
For example, if you are using Material Community Icons or whichever Icon library you are using, just return a component with the icon you want. You can set the headerBackTitleVisible property to false if you don't the text 'Back' in addition to your custom component.

<Stack.Navigator
    screenOptions={{
        headerBackTitleVisible: false,
        headerBackImage: ()=>(<MaterialCommunityIcons name='arrow-left' />),
    }}
>

Screenshot 2020-08-04 at 09 15 39

It worked for me:

import Icon from 'react-native-vector-icons/MaterialIcons'

screenOptions={{
        headerLeft: ({ canGoBack, onPress }) =>
          canGoBack && (
            <Icon
              name="chevron-left"
              onPress={onPress}
              color="#333"
              size={40}
            />
          )
}}
commented

For anyone reading this trying to change the styling or position of the back button, you can use headerLeftContainerStyle to set the left margin for the header
defaultNavigationOptions: { headerLeftContainerStyle: { margin: 20 }, }

header: ({ goBack }) => ({
    left: ( <Icon name={'chevron-left'} onPress={ () => { goBack() } }  /> ),  
}),

Can I show a custom component onPress?

navigationOptions: { title: 'screen 2', headerLeft: () => <TouchableOpacity onPress={() => navigation.goBack()} ><Text style={{ color: '#fff' }}>back</Text></TouchableOpacity> },

It worked for me:

import Icon from 'react-native-vector-icons/MaterialIcons'

screenOptions={{
        headerLeft: ({ canGoBack, onPress }) =>
          canGoBack && (
            <Icon
              name="chevron-left"
              onPress={onPress}
              color="#333"
              size={40}
            />
          )
}}

Thanks, This worked for mee too

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

for my case

I did with headerBackImage

    options={{
      headerShown: true,
      headerTitle: null,
      headerBackTitle: 'title',
      headerBackTitleStyle: {color: '#000', marginLeft: 24},
      headerBackImage: ({goBack}) => <SpecialBack />,
      headerStyle: {
        shadowColor: 'transparent',
      },
    }}

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

This worked smoothly!

<Stack.Navigator
  screenOptions={{
    headerShown: true,
    header: (props) => <CustomAppHeader {...props} />,
  }}
>
  <Stack.Screen name="Thing" component={ThingPage} />
  <Stack.Screen name="Something" component={SomethingPage} />

</Stack.Navigator>;
import {  Pressable, Text, View } from "react-native";

const CustomAppHeader: React.FC<any> = (props) => {

const canGoBack = props.navigation?.canGoBack()
  return (
    <View  style={{ height: 80, justifyContent: "flex-end", alignItems: "flex-start", backgroundColor:"red"}}>
    <Pressable onPress={()=>{
        props.navigation.goBack()
      }}>
     {canGoBack && <Text >Back</Text>}
    </Pressable>
    </View>

  );
};

export default CustomAppHeader;

Refer to the docs here
canGoBack

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.