Login system is an essential part of any application to provide security & customize experience to the user. In this tutorial, we are going to implement it in our react native app by using Firebase. Firebase is a backend solution provided by Google, by using it we can easily create web & mobile application without worrying much about the server and database architecture. Now we are going to do exactly that, we are going to implement an email and password based login system in our react native app without going into the server setup and database management hassle.
Requirements
In this application, we are using react-native CLI and the version of it is 0.63.x, if you’re using 0.60+ then this tutorial will work for you. To provide the navigation between authentication screens and home screen we need to install the react-navigation version 5 and to integrate firebase with our project we have to install react native firebase version 6, and a free tier firebase account.
Installing Dependencies in Our React Native App
After installing creating a new app with react native CLI we need to install other dependencies in our project to get started. In my case I’m using npm to install these, you can also use yarn if you prefer it. In our project root directory we need to run these commands below.
// For react-navigation npm install @react-navigation/native @react-navigation/stack npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view // For react-native-firebase npm install --save @react-native-firebase/app @react-native-firebase/auth
Here we have to install stack navigation along with react navigation. After installing make sure you follow react-navigation official documentation to set up it properly with your project and also for react native firebase as well if you face any issue with it. For iOS run the command below.
npx pod-install ios
Create Project in Firebase Console
To use firebase in our app we need to create a project in firebase console and do some configurations there. For that first, we need to add a project.
Then give your project a name.
After creating the project, within the dashboard go to the Authentication menu and select the Sign-in method tab and enable Email/Password provider.
Then go to Project Settings.
And then we need to add an android and iOS app to our project.
Add Firebase to iOS App
First, we need to provide the project bundle id, app nickname, and click on the Next button. Then we will see a button called Download GoogleService-Info.plist, click on it to download it, and add it to our iOS app by using Xcode.
Right click on the project name in the left pane and add Files to your project.
Select the config file which you just downloaded and add it.
After that add few lines in AppDelegate.m file, open it in Xcode or VS Code or your preferred IDE. It is located in /ios/{projectName}/AppDelegate.m
After other imports at the top of the file, add this
#import <Firebase.h>
Then within the existing didFinishLaunchingWithOptions method add these lines
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Add me --- \/ if ([FIRApp defaultApp] == nil) { [FIRApp configure]; } // Add me --- /\ // ... }
After these configurations, we need to rebuild our project and we need to do pod install as well within the iOS/ directory.
Add Firebase to Android App
Now after iOS setup we will select android and add package name, app nickname similarly as we have done previously. And package name or app bundle id will be the package mentioned in the AndroidManifest.xml file, like below.
Then download the config file by clicking on the Download google-services.json button and place it in /android/app/ directory.
Add google-services dependency to our /android/build.gradle file.
buildscript { dependencies { // ... other dependencies classpath 'com.google.gms:google-services:4.3.3' // Add me --- /\ } }
Then add this line below to /android/app/build.gradle file.
apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' // <- Add this line
That’s it, we are completed with the configuration for both the platform.
Now rebuild the app for android and start implementing the authentication to our react native app.
Create Custom Components
In our login and sign up screen, we will require a few input fields and buttons. To make it easier to implement those screens we will create custom components and import these components into screen components. Now we will create a directory called components/ in our root directory and inside it, we will create FormButton.js and FormInput.js files.
Open FormButton.js file and add these codes below.
import React from 'react'; import {Text, TouchableOpacity, StyleSheet} from 'react-native'; import {windowHeight, windowWidth} from '../utils/Dimentions'; const FormButton = ({buttonTitle, ...rest}) => { return ( <TouchableOpacity style={styles.buttonContainer} {...rest}> <Text style={styles.buttonText}>{buttonTitle}</Text> </TouchableOpacity> ); }; export default FormButton; const styles = StyleSheet.create({ buttonContainer: { marginTop: 10, width: '100%', height: windowHeight / 15, backgroundColor: '#2e64e5', padding: 10, alignItems: 'center', justifyContent: 'center', borderRadius: 3, }, buttonText: { fontSize: 18, fontWeight: 'bold', color: '#ffffff', fontFamily: 'Lato-Regular', }, });
At first, we have imported react and few APIs from react native and then we have imported windowHeight and windowWidth from Dimensions.js file within utils/ directory. This is the helper component that will help us determine device window width & height in any UI component. We are going to create it after this section.
Then we have provided buttonTitle prop to our component and by this, we are going to receive the label of the button and then we have added …rest prop, by this prop we can provide any other TouchableOpacity prop which provided by default to the button such as onPress while calling it. After that, we have added styles for the component.
Now open FormInput.js file to create input fields and use the codes below.
import React from 'react'; import {View, TextInput, StyleSheet} from 'react-native'; import {windowHeight, windowWidth} from '../utils/Dimentions'; import AntDesign from 'react-native-vector-icons/AntDesign'; const FormInput = ({labelValue, placeholderText, iconType, ...rest}) => { return ( <View style={styles.inputContainer}> <View style={styles.iconStyle}> <AntDesign name={iconType} size={25} color="#666" /> </View> <TextInput value={labelValue} style={styles.input} numberOfLines={1} placeholder={placeholderText} placeholderTextColor="#666" {...rest} /> </View> ); }; export default FormInput; const styles = StyleSheet.create({ inputContainer: { marginTop: 5, marginBottom: 10, width: '100%', height: windowHeight / 15, borderColor: '#ccc', borderRadius: 3, borderWidth: 1, flexDirection: 'row', alignItems: 'center', backgroundColor: '#fff', }, iconStyle: { padding: 10, height: '100%', justifyContent: 'center', alignItems: 'center', borderRightColor: '#ccc', borderRightWidth: 1, width: 50, }, input: { padding: 10, flex: 1, fontSize: 16, fontFamily: 'Lato-Regular', color: '#333', justifyContent: 'center', alignItems: 'center', }, });
In this file, we are importing & providing props similar to the previous file, but here I have also used the react-native-vector-icons package to provide an icon to the input field. So make sure you add it to your project properly by following its documentation.
Now create Dimension.js file within utils/ directory and add these codes.
import { Dimensions } from 'react-native'; export const windowWidth = Dimensions.get('window').width; export const windowHeight = Dimensions.get('window').height;
Create Screens
Now create screens/ directory in the root directory of the project and add 3 files.
- LoginScreen.js
- SignupScreen.js
- HomeScreen.js
Create Login Screen
Open the LoginScreen.js file and add these codes.
import React, {useContext, useState} from 'react'; import {View, Text, TouchableOpacity, Image, StyleSheet} from 'react-native'; import FormInput from '../components/FormInput'; import FormButton from '../components/FormButton'; const LoginScreen = ({navigation}) => { const [email, setEmail] = useState(); const [password, setPassword] = useState(); return ( <View style={styles.container}> <Image source={require('../assets/logo.png')} style={styles.logo} /> <Text style={styles.text}>RN Social App</Text> <FormInput labelValue={email} onChangeText={(userEmail) => setEmail(userEmail)} placeholderText="Email" iconType="user" keyboardType="email-address" autoCapitalize="none" autoCorrect={false} /> <FormInput labelValue={password} onChangeText={(userPassword) => setPassword(userPassword)} placeholderText="Password" iconType="lock" secureTextEntry={true} /> <FormButton buttonTitle="Sign In" onPress={() => {}} /> <TouchableOpacity style={styles.forgotButton} onPress={() => {}}> <Text style={styles.navButtonText}>Forgot Password?</Text> </TouchableOpacity> <TouchableOpacity style={styles.forgotButton} onPress={() => navigation.navigate('Signup')}> <Text style={styles.navButtonText}> Don't have an acount? Create here </Text> </TouchableOpacity> </View> ); }; export default LoginScreen; const styles = StyleSheet.create({ container: { backgroundColor: '#f9fafd', flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, logo: { height: 150, width: 150, resizeMode: 'cover', }, text: { fontFamily: 'Kufam-SemiBoldItalic', fontSize: 28, marginBottom: 10, color: '#051d5f', }, navButton: { marginTop: 15, }, forgotButton: { marginVertical: 35, }, navButtonText: { fontSize: 18, fontWeight: '500', color: '#2e64e5', fontFamily: 'Lato-Regular', }, });
Here, first I have imported some APIs from react native and our custom component to create our login UI. In our component, we are receiving navigation prop, which is provided by react-navigation, it’ll help us to navigate other screens. Then we have the logo image for the app which is located within assets/ directory and we are using FormInput and FormButton components and for inputs, we are updating the respective state on onChangeText and then we have the sign-in button, currently it’s not doing anything, but very soon we’ll add it’s functionality.
Create Sign Up Screen
Now it’s time to create a user registration screen in our app and it’ll be similar to LoginScreen, here’s the code below.
import React, {useContext, useState} from 'react'; import {View, Text, TouchableOpacity, Image, StyleSheet} from 'react-native'; import FormInput from '../components/FormInput'; import FormButton from '../components/FormButton'; const SignupScreen = ({navigation}) => { const [email, setEmail] = useState(); const [password, setPassword] = useState(); const [confirmPassword, setConfirmPassword] = useState(); return ( <View style={styles.container}> <Text style={styles.text}>Create an account</Text> <FormInput labelValue={email} onChangeText={(userEmail) => setEmail(userEmail)} placeholderText="Email" iconType="user" keyboardType="email-address" autoCapitalize="none" autoCorrect={false} /> <FormInput labelValue={password} onChangeText={(userPassword) => setPassword(userPassword)} placeholderText="Password" iconType="lock" secureTextEntry={true} /> <FormInput labelValue={confirmPassword} onChangeText={(userPassword) => setPassword(userPassword)} placeholderText="Confirm Password" iconType="lock" secureTextEntry={true} /> <FormButton buttonTitle="Sign Up" onPress={() => {}} /> <View style={styles.textPrivate}> <Text style={styles.color_textPrivate}>By registering, you confirm that you accept our </Text> <TouchableOpacity onPress={() => alert('Terms Clicked!')}> <Text style={[styles.color_textPrivate, {color: '#e88832'}]}>Terms of service</Text> </TouchableOpacity> <Text style={styles.color_textPrivate}> and </Text> <Text style={[styles.color_textPrivate, {color: '#e88832'}]}>Privacy Policy</Text> </View> <TouchableOpacity style={styles.navButton} onPress={() => navigation.navigate('Login')}> <Text style={styles.navButtonText}> Have an account? Sign In </Text> </TouchableOpacity> </View> ); }; export default SignupScreen; const styles = StyleSheet.create({ container: { backgroundColor: '#f9fafd', flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, text: { fontFamily: 'Kufam-SemiBoldItalic', fontSize: 28, marginBottom: 10, color: '#051d5f', }, navButton: { marginTop: 15, }, navButtonText: { fontSize: 18, fontWeight: '500', color: '#2e64e5', fontFamily: 'Lato-Regular', }, textPrivate: { flexDirection: 'row', flexWrap: 'wrap', marginVertical: 35, justifyContent: 'center', }, color_textPrivate: { fontSize: 13, fontWeight: '400', fontFamily: 'Lato-Regular', color: 'grey', }, });
Create Auth Stack Navigator
This is going to be the first stack navigator for our app. It’ll contain both login & signup screen and after adding it we can navigate between these screens.
Now create navigation/ directory in the root of the project and create AuthStack.js file in it.
import React, {useState, useEffect} from 'react'; import {View} from 'react-native'; import {createStackNavigator} from '@react-navigation/stack'; import SignupScreen from '../screens/SignupScreen'; import LoginScreen from '../screens/LoginScreen'; import FontAwesome from 'react-native-vector-icons/FontAwesome'; const Stack = createStackNavigator(); const AuthStack = () => { return ( <Stack.Navigator initialRouteName="Login"> <Stack.Screen name="Login" component={LoginScreen} options={{header: () => null}} /> <Stack.Screen name="Signup" component={SignupScreen} options={({navigation}) => ({ title: '', headerStyle: { backgroundColor: '#f9fafd', shadowColor: '#f9fafd', elevation: 0, }, headerLeft: () => ( <View style={{marginLeft: 10}}> <FontAwesome.Button name="long-arrow-left" size={25} backgroundColor="#f9fafd" color="#333" onPress={() => navigation.navigate('Login')} /> </View> ), })} /> </Stack.Navigator> ); }; export default AuthStack;
Create App Stack Navigator
This is the stack navigator which will hold the main app screens which will be presented to the users after a successful login.
Now for this tutorial, we only have a home screen.
Create Home Screen
Now we need to create the last screen which will be presented to the user after providing correct credentials. This is our app home screen and it’ll be a very basic screen with a simple button and a text in it. So add these codes below to the HomeScreen.js file within screens/ directory
import React, { useContext } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import FormButton from '../components/FormButton'; const HomeScreen = () => { return ( <View style={styles.container}> <Text style={styles.text}>Welcome</Text> <FormButton buttonTitle='Logout' onPress={() => {}} /> </View> ); } export default HomeScreen; const styles = StyleSheet.create({ container: { backgroundColor: '#f9fafd', flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20, }, text: { fontSize: 20, color: '#333333' } });
Now after creating this screen we need to create the stack navigator for it.
For this go to navigation/ directory and create another new file called AppStack.js and add these codes.
import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import HomeScreen from '../screens/HomeScreen'; const Stack = createStackNavigator(); const AppStack = () => { return ( <Stack.Navigator> <Stack.Screen name='Home' component={HomeScreen} /> </Stack.Navigator> ); } export default AppStack;
Create Auth Provider
Now we have to create an authentication provider to authenticate our user and provide the current user state value to all components within the component tree wrapped in it. To create it first we need to create the AuthProvider.js file within the navigation/ directory. Now start by importing state hook and context API from react, along with auth from react-native-firebase
import React, {createContext, useState} from 'react'; import auth from '@react-native-firebase/auth';
Now create AuthContext with the help of createContext API from react.
export const AuthContext = createContext();
After this, we can start creating an auth provider, which will hold all the different functions for the authentication process, such as login, register, and logout.
export const AuthProvider = ({children}) => { const [user, setUser] = useState(null); return ( <AuthContext.Provider value={{ user, setUser, login: async (email, password) => { try { await auth().signInWithEmailAndPassword(email, password); } catch (e) { console.log(e); } }, register: async (email, password) => { try { await auth().createUserWithEmailAndPassword(email, password); } catch (e) { console.log(e); } }, logout: async () => { try { await auth().signOut(); } catch (e) { console.log(e); } }, }}> {children} </AuthContext.Provider> ); };
Here, for this context provider, we have added these 3 functions and 2 state values by which we can access the user state and set it while logging in from other files. And this login, register functions will also be required within the login and signup screens which we have created earlier.
Now to create a new user in firebase, firebase provides createUserWithEmailAndPassword function, which can be fetched from auth() provided by the firebase package which we imported initially and this function requires email and password to create a user, so we are accepting those values in our functions as well. Similar to register while logging in the user will provide email/password, and we will pass it to the signInWithEmailAndPassword function provided by the firebase package.
These functions will be asynchronous functions as we have to wait for the response firebase server, so I have added async function, await for the response, and put it within the try/catch block so that we can fetch any error which may occur in between these process.
Create All Routes
Now we need to create a file that will contain all the routes of the app and it’ll be wrapped within the provider which we have just created.
Start by creating a Routes.js file within the navigation/ directory and adding some imports.
import React, {useContext, useState, useEffect} from 'react'; import {NavigationContainer} from '@react-navigation/native'; import auth from '@react-native-firebase/auth'; import {AuthContext} from './AuthProvider'; import AuthStack from './AuthStack'; import AppStack from './AppStack';
Here, first, we have imported some hooks from react which we will use just now. Then we have imported NavigationContainer from the react-navigation package, we need to wrap all our stack navigators within this container. Then we have the auth from the firebase package and AuthContext from AuthProvider.js this will help use fetch user state and set user state in this file. After this, we have simple imports of our stack navigators.
const Routes = () => { const {user, setUser} = useContext(AuthContext); const [initializing, setInitializing] = useState(true); const onAuthStateChanged = (user) => { setUser(user); if (initializing) setInitializing(false); }; useEffect(() => { const subscriber = auth().onAuthStateChanged(onAuthStateChanged); return subscriber; // unsubscribe on unmount }, []); if (initializing) return null; return ( <NavigationContainer> {user ? <AppStack /> : <AuthStack />} </NavigationContainer> ); };
Now, within useEffect, we are subscribing to the changing authentication state of the user and from the onAuthStateChanged function we are setting the user state. Based on user state we are showing stack navigators, if the user state is setup then we will present AppStack to the user and if the user state is set to null then this app will present AuthStack to the user.
After this, we need to wrap our routes with our provider, for that create index.js file within the navigation/ directory.
import React from 'react'; import { AuthProvider } from './AuthProvider'; import Routes from './Routes'; const Providers = () => { return ( <AuthProvider> <Routes /> </AuthProvider> ); } export default Providers;
Finally, import Providers in our App.js file to see the output of our app.
import React from 'react'; import Providers from './navigation'; const App = () => { return <Providers />; } export default App;
Now after this step we are able to see our app.
To run the app use these commands in the terminal.
# for iOS npx react-native run-ios # for android npx react-native run-android
Add Authentication Functionalities to Screens
Now in SignupScreen.js, we have to access the register function which we have created in our AuthProvider.js file to provide the sign up functionality to the user.
// Other imports import { AuthContext } from '../navigation/AuthProvider'; const SignupScreen = ({navigation}) => { //... const {register} = useContext(AuthContext); return ( <View style={styles.container}> //... <FormButton buttonTitle="Sign Up" onPress={() => register(email, password)} /> //... </View> ); };
Similar to this we need to add the login function to the LoginScreen.js file.
// Other imports import { AuthContext } from '../navigation/AuthProvider'; const LoginScreen = ({navigation}) => { //... const {login} = useContext(AuthContext); return ( <View style={styles.container}> //... <FormButton buttonTitle="Sign In" onPress={() => login(email, password)} /> //... </View> ); };
At last, update our HomeScreen.js file with user information and provide the ability to logout from the app.
// Other imports import { AuthContext } from '../navigation/AuthProvider'; const HomeScreen = () => { const {user, logout} = useContext(AuthContext); return ( <View style={styles.container}> <Text style={styles.text}>Welcome {user.uid}</Text> <FormButton buttonTitle='Logout' onPress={() => logout()} /> </View> ); } //...
To verify, after registering you can go to your firebase console and check users within the authentication menu. You’ll find a newly created user with the details you provided.
Oof, so that’s it.
It’s the complete tutorial for firebase email authentication. In the next tutorial, we will look into Facebook Login and Google Login.
If you want to watch this tutorial in video format then watch it below from my YouTube and consider to subscribe if you like it.
awesome tutorial! your steps were very easy to follow and implement. I will be sending you an email for guidance on another tutorial
I tried to clone this project from git and run into errors. What are exacts steps needed to run this project build and run on a windows PC?
Very good tutorial.
Despite having to use the translator, I think I can use it to assimilate the login that I want to add to one of my applications.
Thank you for sharing your wisdom in such a simple way.
I will follow his lessons on Youtube, since he does not let me subscribe to his Mailing List.
All the best
You forgot to export Routes