Implementing Sidebar Navigation in React Native

In many mobile applications, the Hamburger icon is used for sidebar navigation. When clicked, it opens a sidebar that displays the logged-in user's information, such as their profile photo at the top, and provides links to navigate to different application screens. You can view a demo of this functionality in the Post Video section of the sidebar. Based on your project needs, you can customize the screen links displayed in the sidebar in React Native.

In this guide, I will explain how to create a sidebar menu in a React Native application that opens when the user clicks the Hamburger icon. We will build a Routes class and add screens like Login, Signup, Dashboard, and ForgotPassword to it. The flow of the app will be that once the user logs in, they are redirected to the dashboard screen. We will check whether the user is logged in or not within the dashboard screen, not in the Routes class.

For session storage in the mobile application, we will use the @react-native-async-storage/async-storage package, which is essential for storing user data persistently across app sessions.

Post Video: Sidebar in React Native

									     
import React, { Component } from 'react';
import { Router, Stack, Scene } from 'react-native-router-flux';
import Login from './containers/Login';
import Signup from './containers/Signup';
import Dashboard from './containers/Dashboard';
import ForgotPassword from './containers/ForgotPassword';
export default class Routes extends Component {
	constructor(props) {
		super(props);
	}
	render() {
		return (
			<Router>
				<Stack key="root" hideNavBar={true}>
					<Scene key="login" component={Login} title="Login" />
					<Scene key="signup" component={Signup} title="Register" />
					<Scene key="forgotpassword" component={ForgotPassword} title="ForgotPassword" />
					<Scene key="dashboard" component={Dashboard} title="Dashboard" />
				</Stack>
			</Router>
		)
	}
}										
									

The Login screen in this React Native application utilizes the _bootstrapAsync function, which checks the stored session data to determine if the user is authenticated. If the session data is valid, the user is redirected to the dashboard; if not, they are redirected to the login screen. This functionality ensures that the sidebar is populated with the user’s login information, profile photo, and other relevant details, depending on the project’s needs. The doLogin function leverages Axios to call an API, validating the user’s login credentials. Upon successful authentication, Actions.dashboard() is triggered, passing the user's data, including username, usertypename, profile_photo, and login status, as screen props. These props are then accessed in the Dashboard class, allowing the sidebar to dynamically display the user's details, providing a seamless and personalized experience. This approach is also enhanced by caching the session data for faster access and reducing redundant API calls, ensuring smoother performance across app sessions.

									     
export default class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      email: '',
      password: '',
      forgotEmail: '',
      versionName: 'v1.0.0',
      loading: false,
      isModalVisible: false,
      netStatus: 'none',
      dialogVisible: false,
      errorForgotEmail: "",
      isLoggedIn: false
    }
    this.FlashMessageComponent = React.createRef();
    this._bootstrapAsync = this._bootstrapAsync.bind(this);
  }
  async componentDidMount() {
    NetInfo.fetch().then((connectionInfo) => {
      this.setState({ 'netStatus': connectionInfo.type });
    });
    //NetInfo.addEventListener('connectionChange',this.handleConnectivityChange);
    this._bootstrapAsync();
  }
  _bootstrapAsync = async () => {
    try {
      const saved = await storage.getItem('user');
      const isLoggedIn = await storage.getItem('isLoggedIn');
      const storedItem = JSON.parse(saved);
      if (storedItem !== null && isLoggedIn == 1) {
        if (storedItem.userId > 0 && storedItem.userId != "") {
          this.setState({ 'isLoggedIn': true });
          Actions.dashboard({ 'username': storedItem.username, 'usertypename': storedItem.usertypename, 'isLoggedIn': true });
        } else {
          Actions.login();
        }
      }
    } catch (e) {
      console.warn(e);
    }
  };

  handleConnectivityChange = (connectionInfo) => {
    this.setState({ 'netStatus': connectionInfo.type });
  }

  signup = () => {
    if (!this.state.loading) {
      Actions.signup();
    }
  }
  forgotpassword = () => {
    if (!this.state.loading) {
      Actions.forgotpassword();
    }
  }

  emailChange = (text) => {
    this.setState({
      email: text
    });
  }
  passwordChange = (text) => {
    this.setState({
      password: text
    });
  }
  forgotEmailChange = (text) => {
    this.setState({
      forgotEmail: text
    });
  }

  _toggleModal = () => {
    if (!this.state.loading) {
      this.setState({ isModalVisible: !this.state.isModalVisible });
    }
  }

  _toggleModalOkClick = () => {

  }

  doLogin() {
    const user = {
      email: this.state.email,
      password: this.state.password,
    };

    if (!this.state.loading) {
      if (this.state.netStatus != "none") {

        this.setState({
          loading: true
        });
        axios.post(apiUrl + 'user/login', user, auth).then(response => {
          //var res_data = response.data;
          if (response.data.status == 1) {
            var userA = {
              userId: response.data.profile.userId,
              username: response.data.profile.username,
              email: response.data.profile.email,
              usertype: response.data.profile.usertype,
              usertypename: response.data.profile.usertypename,
              contactno: response.data.profile.contact_no,
              profile_photo: response.data.profile.profile_photo,
            };
            storage.setItem('user', JSON.stringify(userA));
            storage.setItem('isLoggedIn', JSON.stringify(1));
            this.setState({ 'isLoggedIn': true });
            this.email.setNativeProps({
              text: ''
            })
            this.password.setNativeProps({
              text: ''
            })
            this.setState({
              password: '',
              email: ''
            });
            Actions.dashboard({ 'username': response.data.profile.username, 'usertypename': response.data.profile.usertypename, 'isLoggedIn': true, 'profile_photo': response.data.profile.profile_photo });
          } else if (response.data.status == 0) {
            Alert.alert(
              'Tirth Purohit',
              response.data.msg,
              [
                { text: 'OK', onPress: () => console.log('OK Pressed') },
              ],
              { cancelable: true }
            )
            this.setState({ loading: false });
          } else {

            Alert.alert(
              'Tirth Purohit',
              'some error occurred, Please try after few minutes.',
              [
                { text: 'OK', onPress: () => console.log('OK Pressed') },
              ],
              { cancelable: true }
            )
            this.setState({ loading: false });
          }
        }).catch(error => {
          var res_data = error.response;
          if (!res_data.data.status) {
            Alert.alert(
              'Tirth Purohit',
              error.response.data.msg,
              [
                { text: 'OK', onPress: () => console.log('OK Pressed') },
              ],
              { cancelable: true }
            )
          } else {
            Alert.alert(
              'Tirth Purohit',
              'There is some issue , try later: ' + error.response.status,
              [
                { text: 'OK', onPress: () => console.log('OK Pressed') },
              ],
              { cancelable: true }
            )
          }
          this.setState({ loading: false });
        });
      } else {
        Alert.alert(
          'Tirth Purohit',
          netErrorMsg, [
          {
            text: 'OK',
            onPress: () => console.log('OK Pressed')
          },
        ], {
          cancelable: true
        }
        )
        this.setState({ loading: false });
      }
    }
  }
  render() {

    return (
      <View style={styles.mainContainer}>

        <KeyboardAvoidingView
          style={styles.container}
          behavior="padding"
        >
          <View style={(styles.row, styles.imageBox, styles.logo)}>
            <Image style={{ width: 190, height: 32 }} source={{ uri: apiUrl + 'assets/images/signin-logo.png' }} />
          </View>

          <View style={{ width: width - 90, alignItems: 'center', justifyContent: 'center' }}>
            <Text style={styles.pageTitle}>Login Here</Text>
          </View>
          <FlashMessageComponent ref={this.FlashMessageComponent} />
          <TextInput style={styles.inputBox}
            underlineColorAndroid='rgba(0,0,0,0)'
            placeholder="Email"
            placeholderTextColor="#999999"
            selectionColor="#000"
            keyboardType="email-address"
            onSubmitEditing={() => this.password.focus()}
            onChangeText={this.emailChange}
            ref={(input) => {
              this.email = input
            }}
          />
          <TextInput style={styles.inputBox}
            underlineColorAndroid='rgba(0,0,0,0)'
            placeholder="Password"
            secureTextEntry={true}
            selectionColor="#000"
            placeholderTextColor="#999999"
            ref={(input) => this.password = input}
            onChangeText={this.passwordChange}
          />
          <View style={styles.scrollboxActionContainer}>
            <View style={styles.buttonSignup}>
              <TouchableOpacity onPress={
                () => this.doLogin()} >
                <Text style={styles.buttonSignupText}>Login</Text>
              </TouchableOpacity>
            </View>
          </View>
          <View style={{ flexDirection: "row" }}>
            <View style={{ flex: 1, paddingLeft: 10, }}>
              <TouchableOpacity style={{ alignSelf: 'stretch' }} onPress={this.signup}>
                <Text style={styles.button_1}>Create Account?</Text>
              </TouchableOpacity>
            </View>
            <View style={{ borderLeftWidth: 1, borderLeftColor: 'white' }} />
            <View style={{ flex: 1, paddingRight: 10 }}>
              <TouchableOpacity style={{ alignSelf: 'stretch' }} onPress={this.forgotpassword}>
                <Text style={styles.button_2}>Forgot Password?</Text>
              </TouchableOpacity>
            </View>
          </View>

          {(this.state.loading &&
            <View style={styles.loading}>
              <ActivityIndicator size="large" color="#0000ff" animating={this.state.loading} />
            </View>
          )}
        </KeyboardAvoidingView>
        <View style={styles.signupTextCont}>
        </View>
      </View>
    );
  }
}										
									

We have to create a class dashboard ,Which will look like below and which will use createDrawerNavigator,createStackNavigator,createSwitchNavigator to create react native side bar menu. You can see in source code that createDrawerNavigator contain the screens with drawerLabel means what links with icon will be visible to your side bar menu drawer , also you can see AccountSecurity , ProfilePhoto has drawerLabel and drawerIcon as null , It means we don't want to show these screen link in side bar menu but we want to use it as navigation. initialRouteName is AppDashboard with checking weather user logged in or not.

									     
import React from 'react';
import {
  Text, StyleSheet, View,
  Button, Image,
  TouchableOpacity, ActivityIndicator,
  TouchableHighlight, Dimensions, TextInput, Alert, ScrollView
} from 'react-native';
import storage from '@react-native-async-storage/async-storage';
import styles from "../css/dashboard";
import { Ionicons } from '@expo/vector-icons';
import { Avatar } from 'react-native-paper';
import { apiUrl, netErrorMsg, axiosHeaders } from "../config/constant";
import { Actions } from 'react-native-router-flux';
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { createDrawerNavigator, DrawerItems } from 'react-navigation-drawer';
import { Container, Content, Icon, Header, Body, Form } from 'native-base';
import { SafeAreaView } from 'react-native-safe-area-context';
import HomeScreen from "./pages/HomeScreen";
import SettingsScreen from "./pages/SettingsScreen";
import ShareScreen from "./pages/ShareScreen";
import YourAccountsScreen from "./pages/YourAccountsScreen";
import LoginScreen from "./Login";
import AccountSecurityScreen from './pages/AccountSecurityScreen';
import ProfilePhotoScreen from './pages/ProfilePhotoScreen';

const CustomDrawerContentComponent = (props) => {
  return (
    <Container>
      <Header style={styles.drawerHeader}>
        <Body>
          <View style={styles.drawerHeader}>
            <View style={styles.logo, { flex: 2, paddingTop: '5%' }}>
              <Image
                style={{ width: 190, height: 32 }}
                source={{ uri: apiUrl + 'assets/images/signin-logo.png' }} />
              <Text style={{ alignContent: 'center', alignSelf: 'center' }}>Welcome {props.screenProps.username}</Text>
              <Text style={{ alignContent: 'center', alignSelf: 'center' }}> {props.screenProps.usertypename}</Text>
            </View>
            <View style={{ flex: 1, paddingTop: '5%' }}>
              <TouchableOpacity onPress={() => props.navigation.navigate('ProfilePhoto', { ActionPanelActiveTab: 'EPP' })} style={styles.signoutContainer}>
                <Image style={styles.profileImgContainer} source={{ uri: props.screenProps.profile_photo }} />
              </TouchableOpacity>
            </View>
          </View>
        </Body>
      </Header>
      <Content style={{ marginTop: -2 }}>
        <ScrollView>
          <SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
            <DrawerItems {...props} />
            <TouchableOpacity onPress={() =>
              Alert.alert(
                'Log out',
                'Do you want to logout?',
                [
                  { text: 'Cancel', onPress: () => { return null } },
                  {
                    text: 'Confirm', onPress: () => {
                      storage.removeItem('user');
                      storage.removeItem('isLoggedIn');
                      Actions.login();
                    }
                  },
                ],
                { cancelable: false }
              )
            }>
              <Text style={{ margin: 16, fontWeight: 'bold', color: 'red', 'paddingLeft': '15%' }}>
                Logout
		        </Text>
            </TouchableOpacity>
          </SafeAreaView>
        </ScrollView>
      </Content>
    </Container>
  )
};

const MyDrawerNavigator = createDrawerNavigator({
  Home: {
    screen: HomeScreen,
    navigationOptions: ({ navigation }) => {
      return {
        //drawerLabel: () => null,
        //drawerIcon: () => null,
        drawerLabel: function () {
          return (
            <View style={styles.sidebar_hr_bottom}>
              <Text style={styles.navigation_text}>Home</Text>
              <Text style={styles.navigation_bagage_text}>Back to home...</Text>
            </View>
          )
        },
        drawerIcon: ({ tintColor }) => (
          <Ionicons style={styles.icons} name="home-outline" size={24}></Ionicons>
        ),
      }
    }
  },
  YourAccounts: {
    screen: YourAccountsScreen,
    //screen: (props) => <YourAccountsScreen {...props} ActionPanelActiveTab='EP' />,
    navigationOptions: ({ navigation }) => {
      return {
        //drawerLabel: () => null,
        drawerLabel: function () {
          return (
            <View style={styles.sidebar_hr_bottom}>
              <Text style={styles.navigation_text}>Your Accounts</Text>
              <Text style={styles.navigation_bagage_text}>Profile , Password & others...</Text>
            </View>
          )
        },
        drawerIcon: ({ tintColor }) => (
          <Ionicons style={styles.icons} name="person-outline" size={24}></Ionicons>
        ),
      }
    }
  },
  Share: {
    screen: ShareScreen,
    navigationOptions: ({ navigation }) => {
      return {
        //drawerLabel: () => null,
        drawerLabel: function () {
          return (
            <View style={styles.sidebar_hr_bottom}>
              <Text style={styles.navigation_text}>Share</Text>
              <Text style={styles.navigation_bagage_text}>Let others know about the app!</Text>
            </View>
          )
        },
        drawerIcon: ({ tintColor }) => (
          <Ionicons style={styles.icons} name="share-social-outline" size={24}></Ionicons>
        ),
      }
    }
  },
  Settings: {
    screen: SettingsScreen,
    navigationOptions: ({ navigation }) => {
      return {
        //drawerLabel: () => null,
        drawerLabel: function () {
          return (
            <View style={styles.sidebar_hr_bottom}>
              <Text style={styles.navigation_text}>Settings</Text>
              <Text style={styles.navigation_bagage_text}>Change place & preference..</Text>
            </View>
          )
        },
        drawerIcon: ({ tintColor }) => (
          <Ionicons style={styles.icons} name="settings-outline" size={24}></Ionicons>
        ),
      }
    }
  },
  AccountSecurity: {
    screen: AccountSecurityScreen,
    //screen: (props) => <AccountSecurityScreen {...props} ActionPanelActiveTab='EAS' />,
    navigationOptions: ({ navigation }) => {
      return {
        drawerLabel: () => null,
        drawerIcon: () => null,
      }
    }
  },
  ProfilePhoto: {
    screen: ProfilePhotoScreen,
    //screen: (props) => <ProfilePhotoScreen {...props} />,
    navigationOptions: ({ navigation }) => {
      return {
        drawerLabel: () => null,
        drawerIcon: () => null,
      }
    }
  }
},
  {
    drawerPosition: 'left',
    drawerType: 'front',
    initialRouteName: 'Home',
    drawerBackgroundColor: '#FFF',
    drawerWidth: 300,
    contentComponent: CustomDrawerContentComponent,
    contentOptions: {
      activeTintColor: '#2EB6AE',
      inactiveTintColor: '#939393',
    }
  });

const AppStack = createStackNavigator({
  Home: {
    screen: MyDrawerNavigator,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
      headerTransparent: true
    }
  },
  YourAccounts: {
    screen: MyDrawerNavigator,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
    }
  },
  Share: {
    screen: MyDrawerNavigator,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
    }
  },
  Settings: {
    screen: MyDrawerNavigator,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
    }
  },
  AccountSecurity: {
    screen: MyDrawerNavigator,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
    }
  },
  ProfilePhoto: {
    screen: MyDrawerNavigator,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
    }
  },
});
const AuthStack = createStackNavigator({
  Login: {
    screen: LoginScreen,
    navigationBarStyle: { navBarHidden: true },
    navigationOptions: {
      headerShown: false,
    }
  },
});

const AppNavigator = createSwitchNavigator(
  {
    AppDashboard: AppStack,
    Auth: AuthStack,
  },
  {
    initialRouteName: 'AppDashboard',
  },
);

const AppContainer = createAppContainer(AppNavigator);
export default class Dashboard extends React.Component {
  render() {
    return (
      this.props.isLoggedIn ? <AppContainer screenProps={{ username: this.props.username, usertypename: this.props.usertypename, isLoggedIn: this.props.isLoggedIn, profile_photo: this.props.profile_photo }} /> : <LoginScreen />
    );
  }
}										
									

Finally we will create a class which will contain Hamburger icon and toggleDrawer function so when you press on it sidebar toggle accordingly.

									     
import React, { Component } from 'react';
import { Text, View, TouchableOpacity, Image } from 'react-native';
import { apiUrl } from "../config/constant";
export default class HeaderComponent extends Component {
  toggleDrawer = () => {
    this.props.navigationProps.toggleDrawer();
  }
  render() {
    return (
      <View>
        <TouchableOpacity
          style={{
            position: 'absolute',
            top: 5,
            left: 10,
            display: 'flex',
            flexDirection: 'column',
            justifyContent: 'space-around',
            background: 'transparent',
            border: 'none',
            zIndex: 10
          }}
          onPress={() => {
            this.props.navigation.openDrawer();
          }}
        >
          <Image source={{ uri: apiUrl + 'assets/images/menu-rounded.png' }}
            style={{ width: 25, height: 25, marginLeft: -2 }}
          />
        </TouchableOpacity>
        <View style={{ top: 9, alignContent: 'center', alignSelf: 'center' }}>
          <Text
            style={{ color: '#000080', alignContent: 'center', alignSelf: 'center' }}>
            {this.props.showName}
          </Text>
        </View>
      </View>
    );
  }
}