Never miss an article about web development, JavaScript and self-growth.

Take Part

A Firebase in React Tutorial for Beginners [2018]

I am writing The Road to React with Firebase book which I intend to release end of this year. If you are interested in grabbing a copy of the book, you can receive updates for it. Feel encouraged to leave me any feedback for improving the content. You can even contribute to it by editing this article on GitHub. I want to create a high quality book about Firebase in React similar to The Road to learn React

This comprehensive tutorial walks you through a real-world application using React and Firebase. React is used to display applications in web browsers and to store local state in components, while Firebase is used for authentication, authorization, and managing a realtime database.

After you’ve mastered the basics of React, I always recommend moving on to advanced topics like authentication, authorization, and connecting React applications to databases. These operations make up the fundamentals real business applications need. Don’t worry about implementing the backend application that manages it all yourself, as Firebase provides the perfect alternative. I have seen real businesses go from zero to profitable with only React and Firebase as their tools, myself included. No backend application with Node.js was needed, and this tutorial was created to show you how.

50% of this tutorial’s outcome can seen here. Security reasons prevent me from showing everything there, though the remaining material can be found in the book.

To keep the guide updated, here is a list of the primary libraries and their versions used in this tutorial:

React 16.6

React Router 4

Firebase 4

Please let me know if the tutorial needs any updates for others learning about the topic, and don’t hesitate to point out improvements in the comments, or you can visit the article directly on GitHub to open issues or pull requests.

Requirements

The requirements for this tutorial are a working editor or IDE/terminal, and recent versions of node and npm. You should have learned about React in the first place. The Road to learn React is a free ebook that provides all the fundamentals of React. You will build a larger application in plain React, and transition from JavaScript ES5 to JavaScript ES6 and beyond. This tutorial will not dive into all the details taught in the ebook, so take the chance to grab your copy of it to learn those first.

The Road to learn React

Build a Hacker News App along the way. No setup configuration. No tooling. No Redux. Plain React in 200+ pages of learning material. Pay what you want like 50.000+ readers.

React Application Setup: create-react-app

There are two ways to begin the lesson for this application: either follow my guidance in this section; or find a starter project in this GitHub repository and follow its installation instructions. This section will show how to set up a project from scratch, whereas the starter project grants instant access without setting up the folder/file structure yourself.

The application we are going to build with React and Firebase will be set up with Facebook’s official React boilerplate project, called create-react-app. You can install it globally on the command line once, after which it becomes available whenever you need it.

npminstall-gcreate-react-app

After the installation, set up your project with it on the command line whereas the name for the project is up to you:

Now you have the following command on your command line to start your application. You can start your application and visit it in the browser:

npmstart

Now we’ll set up the project for our needs. First, get rid of the files from the bootstrapped React project, since we won’t be using them. From the command line, head to your src/ folder and execute it:

cdsrcrmApp.jsApp.test.jsApp.csslogo.svg

Second, create a components/ folder in your application’s src/ folder on the command line. This is where all your components will be implemented. Also, the App component that you have removed in the previous step will be recreated here.

mkdircomponents

Create a dedicated folder for each component we will implement for this application :

In each folder, create an index.js file for the component. Navigate into a folder, create the file, and navigate out again. Repeat these steps for every component. You can choose to name your folders/files differently, but that’s how I liked to do it for my applications.

cdApptouchindex.jscd..

Next, implement a basic component for each file you created. For the App component in src/components/App/index.js, it could look like the following:

The folder should be located next to src/components/. Move into src/constants/ and create two files for the routing and roles later:

cdconstantstouchroutes.jsroles.jscd..

The application is set up, and you can verify this by running it on the command line and accessing it through a browser. Check the starter project on GitHub I linked in the beginning of this section to verify whether you have set up everything properly.

Exercises:

Familiarize yourself with the folder structure of a project.

Optionally, introduce a test for your App component and test the application.

React Router for Firebase Auth

Since we are building a larger application in the following sections, it would be great to have a couple of pages (e.g. landing page, account page, admin page, sign up page, sign in page) to split the application into multiple URLs (e.g. /landing, /account, /admin). These URLs or subpaths of a domain are called routes in a client-side web application. Let’s implement the routing with React Router before we dive into Firebase for the realtime database and authentication/authorization.

The application should have multiple routes. For instance, a user should be able to visit a landing page, and also use sign up and sign in pages to enter the application as an authenticated user. If a user is authenticated, it is possible to visit protected pages like account or admin pages. You can consolidate all the routes of your application in a well-defined src/constants/routes.js constants file:

First, you will have a sign up page (register page) and a sign in page (login page). You can take any web application as the blueprint to structure these routes for well-rounded authentication. Take the following scenario: A user visits your web application, is convinced by your service, and finds the button in the top-level navigation bar to sign in to your application. But the user has no account yet, so a sign up button is presented as an alternative on the sign in page.

Second, there will be a landing page and a home page. The landing page is your default route (e.g. http://yourdomain/). That’s the place where a user ends up when visiting your web application. The user doesn’t need to be authenticated to go this route. On the other hand, the home page is a protected route, which users can only access if they have been authenticated. You will implement the protection of the route using authorization mechanisms for this application.

Third, next to the home page, there will be protected account page and admin page as well. On the account page, a user can reset or change a password. It is secured by authorization as well, so it is only reachable for authenticated users. On the admin page, a user authorized as admin will be able to delete users. The admin page is protected on a more fine-grained level, because it is only accessible for authenticated admin users.

Lastly, the password forget component will be exposed on another non-protected page, a password forget page, as well. It is used for users who are not authenticated and forgot about their password.

We’ve completed the routes for this React with Firebase application. I find it exciting to build a well-rounded application with you, because it can be used as a boilerplate project that gives you authentication, authorization, and a database. These are the foundations for web-based applications.

Now, all these routes need to be accessible to the user. First, you need a router for your web application, which is responsible to map routes to React components. React Router is a popular package to enable routing, so install it on the command line:

npminstallreact-router-dom

The best way to start is implementing a Navigation component that will be used in the App component. The App component is the perfect place to render the Navigation component, because it always renders the Navigation component but replaces the other components (pages) based on the routes. Basically, the App component is the container where all your fixed components are going (e.g. navigation bar, side bar, footer), but also your components that are displayed depending on the route in the URL (e.g. account page, login page, password forget page).

First, the App component will use the Navigation component that is not implemented yet. Also, it uses the Router component provided by React Router. The Router makes it possible to navigate from URL-to-URL on the client-side application without another request to a web server for every route change. The application is only fetched once from a web server, after which all routing is done on the client-side with React Router.

Second, implement the Navigation component. It uses the Link component of React Router to enable navigation to different routes. These routes were defined previously in your constants file. Let’s import all of them and give every Link component a specific route.

Now, run your application again and verify that the links show up in your browser, and that once you click a link, the URL changes. Notice that even though the URL changes, the displayed content doesn’t change. The navigation is only there to enable navigation through your application. But no one knows what to render on each route. That’s where the route to component mapping comes in. In your App component, you can specify which components should show up according to corresponding routes with the help of the Route component from React Router.

If a route matches a path prop, the respective component will be displayed; thus, all the page components in the App component are exchangeable by changing the route, but the Navigation component stays fixed independently of any route changes. This is how you enable a static frame with various components (e.g. Navigation) around your dynamic pages driven by routes.

Previously, you created basic components for each page component used by our routes. Now you should be able to start the application again. When you click through the links in the Navigation component, the displayed page component should change according to the URL. The routes for the PasswordForget and SignUp components are not used yet in the Navigation component, but will be defined elsewhere later. For now, you have successfully implemented fundamental routing for this application.

Exercises:

Firebase in React

The main focus here is using Firebase in React for the application we’ll build together. Firebase, bought by Google in 2014, enables realtime databases in React, which can be used for extensive authentication and authorization, and even for deployment. You can build real-world applications with React and Firebase without worrying about implementing a backend application. All the things a backend application would handle, like authentication and a database, is handled by Firebase. Many businesses use React and Firebase to power their applications, as it is the ultimate combination to launch an MVP.

To start, sign up on the official Firebase website. After you have created a Firebase account, you should be able to create projects and be granted access to the project dashboard. We’ll begin by creating a project for this application on their platform whereas the project can have any name. In the case of this application, run it on the free pricing plan. If you want to scale your application later, you can change the plan. Follow this visual Firebase setup and introduction guide to learn more about Firebase’s dashboard and features.

Next, find the project’s configuration in the settings on your dashboard. There, you’ll have access to all the necessary information: secrets, keys, ids and other details to set up your application. Copy these in the next step to your React application.

Sometimes the Firebase website doesn’t make it easy to find this page. Since it’s moved around with every iteration of the website, I cannot give you any clear advice where to find it on your dashboard. This is an opportunity to familiarize yourself with Firebase project’s dashboard while you search for the configuration.

Now that we’ve completed the Firebase setup, you can return to your application in your editor/IDE to add the Firebase configuration. First, install Firebase for your application on the command line:

npminstallfirebase

Next, we’ll create a new file for the Firebase setup. We will use a JavaScript class to encapsulate all Firebase functionalities, realtime database, and authentication, as a well-defined API for the rest of the application. You need only instantiate the class once, after which it can use it then to interact with the Firebase API, your custom Firebase interface.

Let’s start by copying the configuration from your Firebase project’s dashboard on their website to your application as a configuration object in a new src/components/Firebase/firebase.js file. Make sure to replace the capitalized keys with the corresponding keys from your copied configuration:

You can also use environment variables in React applications, but you have to use the REACT_APP prefix when you use create-react-app to set up the application. If you are not using create-react-app, you can name them as you see fit.

Now you can define the environmental variables in a new .env file in your project’s root folder. The .env file can also be added to your .gitginore file (in case you are using git), so your Firebase credentials are not exposed on a platform like GitHub.

You can define the configuration inline or as environment variables. Environmental variables are more secure, and should be used when uploading your project to a version control system like GitHub, though we will be continuing with the Firebase setup.

Import firebase from the library you installed earlier, and then use it within your new Firebase class to initialize firebase with the configuration:

That’s all that is needed for a firebase configuration in your application. Optionally, you can create a second Firebase project on the Firebase website to have one project for your development environment and one project for your production environment. That way, you never mix data in the Firebase database in development mode with data from your deployed application (production mode). If you decide to create projects for both environments, use the two configuration objects in your Firebase setup and decide which one you take depending on the development/production environment:

An alternate way to implement this is to specify a dedicated .env.development and .env.production file for both kinds of environment variables in your project. Each file is used to define environmental variables for the matching environment. Defining a configuration becomes straightforward again, because you don’t have to select the correct configuration.

Whether you used environment variables, defined the configuration inline, used only one Firebase project, or multiple projects for each environment, you configured Firebase for your React application. The next section will show you how a Firebase instance created from the Firebase class is used in React.

Exercises:

Provide Firebase in React

You created a Firebase class, but you are not using it in your React application yet. In this section, we’ll connect the Firebase with the React world. The simple approach is to create a Firebase instance with the Firebase class, and then import the instance (or class) in every React component where it’s needed. That’s not the best approach though, for two reasons:

It is more difficult to test your React components.

It is more error prone, because Firebase should only be initialized once in your application (singleton) and by exposing the Firebase class to every React component, you could end up by mistake with multiple Firebase instances.

An alternative way is to use React’s Context API to provide a Firebase instance once at the top-level of your component hierarchy. Create a new src/components/Firebase/context.js file in your Firebase module and provide the following implementation details:

The createContext() function essentially creates two components. The FirebaseContext.Provider component is used to provide a Firebase instance once at the top-level of your React component tree, which we will do in this section; and the FirebaseContext.Consumer component is used to retrieve the Firebase instance if it is needed in the React component. For a well-encapsulated Firebase module, we’ll define a index.js file in our Firebase folder that exports all necessary functionalities (Firebase class, Firebase context for Consumer and Provider components):

The Firebase Context from the Firebase module (folder) is used to provide a Firebase instance to your entire application in the src/index.js file. You only need to create the Firebase instance with the Firebase class and pass it as value prop to the React’s Context:

Doing it this way, we can be assured that Firebase is only instantiated once and that it is injected via React’s Context API to React’s component tree. Now, every component that is interested in using Firebase has access to the Firebase instance with a FirebaseContext.Consumer component. Even though you will see it first-hand later for this application, the following code snippet shows how it would work:

Firebase and React are now connected, the fundamental step to make the layers communicate with each other. Next, we will implement the interface for the Firebase class on our side to communicate with the Firebase API.

Exercises:

Firebase's Authentication API

In the previous section, you created a Firebase project on the official Firebase website. This section will implement the interface of your Firebase class that enables communication between the class and the Firebase authentication API. In the sections afterward, you will use the interface of the Firebase class in your React components.

First, we need to activate one of the available authentication providers on Firebase’s website. On your project’s Firebase dashboard, you can find a menu item which says “Authentication”. Select it and click “Sign-In Method” menu item afterward. There you can enable the authentication with Email/Password. Mainly we will cover the basic authentication with email and password here, but feel free to add other authentication methods (e.g. Facebook Sign-In) later.

Second, we will implement the authentication API for our Firebase class. Import and instantiate the package from Firebase responsible for all the authentication in your src/components/Firebase/firebase.js file:

Let’s define all the authentication functions as class methods step by step. They will serve our communication channel from the Firebase class to the Firebase API. First, the sign up function (registration) takes email and password parameters for its function signature and uses an official Firebase API endpoint to create a user.

These endpoints are called asynchronously, and they will need to be resolved later, as well as error handling. For instance, it is not possible to sign in a user who is not signed up yet since the Firebase API would return an error.

You don’t need to pass any argument to the sign out function because Firebase knows about the currently authenticated user. If no user is authenticated, nothing will happen when this function is called.

That’s the authentication interface for your React components that will connect to the Firebase API. It covers all the use cases for this application. In the next section, we will consume all the methods of your Firebase class in your React components.

Exercises:

Sign Up with React and Firebase

We set up all the routes for your application, configured Firebase and implemented the authentication API for your Firebase class. Now it’s time to use the authentication functionalities in your React components, which we’ll build from scratch. I try to put most of the code in one block, because the components are not too small, and splitting them up step by step might be too verbose. Nevertheless, I will guide you through each code block afterward. The code blocks for forms can become repetitive, so they will be explained.

Let’s start with the sign up page (registration page). It consists of the page, a form, and a link. The form is used to sign up a new user to your application, but the link will be used on the sign in page (login page) if a user has no account. It is a redirect to the sign up page, but not used on the sign up page itself. Implement the src/components/SignUp/index.js file the following way:

The SignUpForm component is the only React class component in this file, because it has to manage the form state in React’s local state. There are two pieces missing in the current SignUpForm component: the form content in the render method in terms of input fields to capture the information (email address, password, etc.) of a user and the implementation of the onSubmit class method when a user signs up eventually.

First, let’s initialize the state of the component. It will capture the user information such as username, email, and password. There will be a second password field/state for a password confirmation. In addition, there is an error state to capture an error object in case of the sign up request to the Firebase API fails. The state is initialized by an object destructuring. This way, we can use the initial state object to reset the state after a successful sign up.

Let’s take the last implemented code block apart. All the input fields implement the unidirectional data flow of React; thus, each input field gets a value from the local state and updates the value in the local state with a onChange handler. The input fields are controlled by the local state of the component and don’t control their own states. They are controlled components.

In the last part of the form, there is an optional error message from an error object. The error objects from Firebase have this message property by default, so you can rely on it to display the proper text for your application’s user. However, the message is only shown when there is an actual error using a conditional rendering.

One piece in the form is missing: validation. Let’s use an isInvalid boolean to enable or disable the submit button.

The user is only allowed to sign up if both passwords are the same, and if the username, email and at least one password are filled with a string. This is password confirmation in a common sign up process. You should be able to visit the /signup route in your browser after starting your application to confirm that the form with all its input fields shows up. You should also be able to type into it (confirmation that the local state updates are working) and able to enable the submit button by providing all input fields a string (confirmation that the validation works).

What’s missing in the component is the onSubmit() class method, which will pass all the form data to the Firebase authentication API via your authentication interface in the Firebase class.

The code is not working yet, but let’s break down what we have so far. All the necessary information passed to the authentication API can be destructured from the local state. You will only need one password property, because both password strings should be the same after the validation.

Next, call the sign up function defined in the previous section in the Firebase class, which takes the email and the password property. The username is not used yet for the sign up process, but will be used later.

If the request resolves successfully, you can set the local state of the component to its initial state to empty the input fields. If the request is rejected, you will run into the catch block and set the error object in the local state. An error message should show up in the form due to the conditional rendering in your component’s render method.

Also, the preventDefault() method on the event prevents a reload of the browser which otherwise would be a natural behavior when using a submit in a form. Note that the signed up user object from the Firebase API is available in the callback function of the then block in our request. You will use it later with the username.

You may have also noticed that one essential piece is missing: We didn’t make the Firebase instance available in the SignUpForm component’s props yet. Let’s change this by utilizing our Firebase Context in the SignUpPage component, and by passing the Firebase instance to the SignUpForm.

Now the registration of a new user should work. However, I’d like to make one improvement on how we access the Firebase instance here. Rather than using a render prop component, which is automatically given with React’s Context Consumer component, it may be simpler to use a higher-order component. Let’s implement this higher-order component in the src/components/Firebase/context.js:

Now, instead of using the Firebase Context directly in the SignUpPage, which doesn’t need to know about the Firebase instance, use the higher-order component to wrap your SignUpForm. Afterward, the SignUpForm has access to the Firebase instance via the higher-order component. It’s also possible to use the SignUpForm as standalone without the SignUpPage, because it is responsible to get the Firebase instance via the higher-order component.

When a user signs up to your application, you want to redirect the user to another page. It could be the user’s home page, a protected route for only authenticated users. You will need the help of React Router to redirect the user after a successful sign up.

Let’s take the previous code block apart again. To redirect a user to another page programmatically, we need access to React Router to redirect the user to another page. Fortunately, the React Router node package offers a higher-order component to make the router properties accessible in the props of a component. Any component that goes in the withRouter() higher-order component gains access to all the properties of the router, so when passing the enhanced SignUpFormBase component to the withRouter() higher-order component, it has access to the props of the router. The relevant property from the router props is the history object, because it allows us to redirect a user to another page by pushing a route to it.

The history object of the router can be used in the onSubmit() class method eventually. If a request resolves successfully, you can push any route to the history object. Since the pushed /home route is defined in our App component with a matching component to be rendered, the displayed page component will change after the redirect.

There is one improvement that we can make for the higher-order components used for the SignUpForm. Nesting functions (higher-order components) into each other like we did before can become verbose. A better way is to compose the higher-order components instead. To do this, install recompose for your application on the command line:

npminstallrecompose

You can use recompose to organize your higher-order components. Since the higher-order components don’t depend on each other, the order doesn’t matter. Otherwise, it may be good to know that the compose function applies the higher-order components from right to left.

Run your application again. If you signed up a user successfully, it should redirect to the home page. If the sign up fails, you should see an error message. Try to sign up a user with the same email address twice and verify that a similar error message shows up: “The email address is already in use by another account.”. Congratulations, you signed up your first user via Firebase authentication.

Exercises:

Sign In with React and Firebase

A sign up automatically results in a sign in/login by the user. We cannot rely on this mechanic, however, since a user could be signed up but not signed in. Let’s implement the login with Firebase now. It is similar to the sign up mechanism and components, so this time we won’t split it into so many code blocks. Implement the src/components/SignIn/index.js file:

It is almost the same as the sign up form. Its input fields capture all the necessary information like username and password. A validation step makes sure the email and password are set before performing the request by enabling or disabling the submit button. The authentication API is used again, this time with a function to sign in the user rather than sign them up. If sign in succeeds, the local state is updated with the initial state and the user is redirected again. If the sign in fails, an error object is stored in the local state and an error message appears. The SignUpLink, which was defined earlier in the SignUp module, is used on the sign in page. It lets users sign up if they don’t have an account, and it is found on the sign in page.

Exercises:

Familiarize yourself with the SignIn and SignInForm components.

If they are mysterious to you, checkout the previous section with the implementation of the SignUpForm again

Sign Out with React and Firebase

To complete the authentication loop, next we’ll implement the sign out component. The component is just a button that appears within the Navigation component. Since we can use the previously-defined authentication API to sign out a user, passing functionality to a button in a React component is fairly straightforward. Implement the SignOut in the src/components/SignOut/index.js file:

Exercises:

Session Handling with Firebase/React

This section is the most important one for the authentication process. You have all the components needed to fulfil an authentication roundtrip in React, and all that’s missing is an overseer for the session state. Logic regarding the current authenticated user needs to be stored and made accessible to other components. This is often the point where developers start to use a state management library like Redux or MobX. Without these, we’ll make due using global state instead of state management libraries.

Since our application is made under the umbrella of App component, it’s sufficient to manage the session state in the App component using React’s local state. The App component only needs to keep track of an authenticated user (session). If a user is authenticated, store it in the local state and pass the authenticated user object down to all components that are interested in it. Otherwise, pass the authenticated user down as null. That way, all components interested in it can adjust their behavior (e.g. use conditional rendering) based on the session state. For instance, the Navigation component is interested because it has to show different options to authenticated and non authenticated users. The SignOut component shouldn’t show up for a non authenticated user, for example.

We handle session handling in the App component in the src/components/App/index.js file. Because the component handles local state now, you have to refactor it to a class component. It manages the local state of a authUser object, and then passes it to the Navigation component.

The helper function onAuthStateChanged() receives a function as parameter that has access to the authenticated user. Also, the passed function is called every time something changes for the authenticated user. It is called when a user signs up, signs in, and signs out. If a user signs out, the authUser object becomes null, so the authUser property in the local state is set to null and all components depending on it adjust their behavior (e.g. display different options like the Navigation component).

We also want to avoid memory leaks that lead to performance issues, so we’ll remove the listener if the component unmounts.

Start your application and verify that your sign up, sign in, and sign out functionality works, and that the Navigation component displays the options depending on the session state (authenticated user).

You have successfully implemented the authentication process with Firebase in React. Everything in the following sections is considered extra, to improve the developer’s experience and add a couple of useful features along the way.

Exercises:

Session Handling with Higher-Order Components

We added a basic version of session handling in the last section. However, the authenticated user still needs to be passed down from the App component to interested parties. That can become tedious over time, because the authenticated user has to be passed through all components until it reaches all the leaf components. You used the React Context API to pass down the Firebase instance to any component before. Here, you will do the same for the authenticated user. In a new src/components/Session/context.js file, place the following new React Context for the session (authenticated user):

The application works the same as before, except any component can simply use React’s Context to consume the authenticated user. To keep the App component clean and concise, I like to extract the session handling for the authenticated user to a separate higher-order component in a new src/components/Session/withAuthentication.js file:

As you can see, it also uses the new React Context to provide the authenticated user. The App component will not be in charge of it anymore. Next, export the higher-order component from the src/components/Session/index.js file, so that it can be used in the App component after:

The App component becomes a function component again, without the additional business logic for the authenticated user. Now, it uses the higher-order component to make the authenticated user available for all other components below of the App component:

Start the application and verify that it still works. You didn’t change any behavior in this section, but shielded away the more complex logic into a higher-order component. Also, the application now passes the authenticated user implicitly via React’s Context, rather than explicitly through the component tree using props.

Exercises:

Check again your Firebase Context and higher-order component implementation in the src/components/Firebase module, which is quite similar to what you have done in this section.

Password Reset and Password Change with Firebase

Let’s take a step back from the higher-order components, React Context API, and session handling. In this section, we will implement two additional features available in the Firebase authentication API, the ability to retrieve (password forget) and change a password.

Password Forget

Let’s start by implementing the password forget feature. Since you already implemented the interface in your Firebase class, you can use it in components. The following file adds most of the password reset logic in a form again. We already used a couple of those forms before, so it shouldn’t be different now. Add this in the src/components/PasswordForget/index.js file:

The code is verbose, but it it’s no different from the sign up and sign in forms from previous sections. The password forget uses a form to submit the information (email address) needed by the Firebase authentication API to reset the password. A class method (onSubmit) ensures the information is send to the API. It also resets the form’s input field on a successful request, and shows an error on an erroneous request. The form is validated before it is submitted as well. The file implements a password forget link as a component which isn’t used directly in the form component. It is similar to the SignUpLink component that we used on in the SignInPage component. This link is the same, and it’s still usable. If a user forgets the password after sign up, the password forget page uses the link in the src/components/SignIn/index.js file:

The password forget page is already matched in the App component, so you can drop the PasswordForgetLink component in the sign in page and know the mapping between route and component is complete. Start the application and reset your password. It doesn’t matter if you are authenticated or not. Once you send the request, you should get an email from Firebase to update your password.

Password Change

Next we’ll add the password change feature, which is also in your Firebase interface. You only need a form component to use it. Again, the form component isn’t any different from the sign in, sign up, and password forget forms. In the src/components/PasswordChange/index.js file add the following component:

The component updates its local state using onChange handlers in the input fields. It validates the state before submitting a request to change the password by enabling or disabling the submit button, and it shows again an error message when a request fails.

So far, the PasswordChangeForm is not matched by any route, because it should live on the Account page. The Account page could serve as the central place for users to manage their account, where it shows the PasswordChangeForm and PasswordResetForm, accessible by a standalone route. You already created the src/components/Account/index.js file and matched the route in the App component. You only need to implement it:

The Account page doesn’t have any business logic. It uses the password forget and password change forms in a central place. In this section, your user experience improved significantly with the password forget and password change features, handling scenarios where users have trouble remembering passwords.

Exercises:

Consider ways to protect the Account page and make it accessible only for authenticated users.

Protected Routes in React with Authorization

When you sign out on the home or account page, there is no redirect, even though these pages are only accessible for authenticated users. There is no reason to show a non authenticated user the account or home page, however, so you will implement a protection for these routes in case a user signs out called authorization. The protection is a broad-grained authorization, which checks for authenticated users. If none is present, it redirects from a protected to a public route; else, it will do nothing. The condition is defined as:

constcondition=authUser=>authUser!=null;// short versionconstcondition=authUser=>!!authUser;

In contrast, a more fine-grained authorization could be a role-based or permission-based authorization:

Fortunately, we implement it in a way that lets you define the authorization condition (predicate) with flexibility. You can use a more generalized authorization rule, permission-based or role-based authorizations.

Like the withAuthentication higher-order component, there is a withAuthorization higher-order component to shield the authorization business logic from your components. It can be used on any component that needs to be protected with authorization (e.g. home page, account page). Let’s start to add the higher-order component in a new src/components/Session/withAuthorization.js file:

The higher-order component gets a condition function passed as parameter. You can decide if it should be a broad or fine-grained (role-based, permission-based) authorization rule. Second, it has to decide based on the condition whether it should redirect to a public page (public route), because the user isn’t authorized to view the current protected page (protected route). Let’s paste the implementation details for the higher-order component and go through it step-by-step:

The render method displays the passed component (e.g. home page, account page) that should be protected by this higher-order component. We will refine this later. The real authorization logic happens in the componentDidMount() lifecycle method. Like the withAuthentication() higher-order component, it uses the Firebase listener to trigger a callback function every time the authenticated user changes. The authenticated user is either a authUser object or null. Within this function, the passed condition() function is executed with the authUser. If the authorization fails, for instance because the authenticated user is null, the higher-order component redirects to the sign in page. If it doesn’t fail, the higher-order component does nothing and renders the passed component (e.g. home page, account page). To redirect a user, the higher-order component has access to the history object of the Router using the in-house withRouter() higher-order component from the React Router library.

Remember to export the higher-order component from your session module into the src/components/Sessions/index.js file:

In the next step, you can use the higher-order component to protect your routes (e.g. /home and /account) with authorization rules using the passed condition() function. To keep it simple, the following two components are only protected with a broad authorization rule that checks if the authUser is not null. First, enhance the HomePage component with the higher-order component and define the authorization condition for it:

The improvement in the render method was needed to avoid showing the protected page before the redirect happens. You want to show nothing if the authenticated user doesn’t meet the condition’s criteria. It’s fine if the listener is too late to redirect the user, because the higher-order component didn’t show the protected component.

Both routes are protected now, so we can render properties of the authenticated user in the AccountPage component without a null check for the authenticated user. You know the user should be there, otherwise the higher-order component would redirect to a public route.

You can try it by signing out from your application and trying to access the /account or /home routes. Both should redirect you to the /signin route. It should also redirect you automatically when you stay on one of the routes.

You can imagine how this technique gives control over authorizations, not just by broader authorization rules, but more specific role-based and permission-based authorizations. For instance, an admin page available for users with the admin role could be protected as follows:

We’ll implement a similar authorization for this application later. You have successfully implemented a full-fledged authentication mechanisms with Firebase in React, added neat features such as password reset and password change, and protected routes with dynamic authorization conditions.

Exercises:

Research yourself how a role-based or permission-based authorization could be implemented

Firebase Realtime Database in React

So far, only Firebase knows about your users. There is no way to retrieve a single user or a list of users. They are stored internally by Firebase to keep the authentication secure. That’s good, because you are never involved in storing sensible data like passwords. However, you can introduce the Firebase realtime database to keep track of user entities, otherwise you can’t associate other domain entities (e.g. a message, book, invoice) created by your users to your users. This section will explain how to store users in your realtime database in Firebase.

First, initialize the realtime database API for your Firebase class as you did earlier for the authentication API:

The paths in the ref() method match the location where your entities (users) will be stored in Firebase’s realtime database API. If you delete a user at “users/5”, the user with the identifier 5 will be removed from the database. If you create a new user at “users”, Firebase creates the identifier for you and assigns all the information you pass for the user. The paths follow the REST philosophy where every entity (e.g. user, message, book, author) is associated with a URI, and HTTP methods are used to create, update, delete and get entities. In Firebase, the RESTful URI becomes a simple path, and the HTTP methods become Firebase’s API.

Exercises:

Manage Users with Firebase's Realtime Database in React

Now, use these references in your React components to create and get users from Firebase’s realtime database. The best place to add user creation is the SignUpForm component, as it is the most natural place in the database after signing up via the Firebase authentication API. Add another API request to create a user when the sign up is successful. In src/components/SignUp/index.js file:

There are two important things happening for a new sign up via the submit handler:

(1) It creates a user in Firebase’s internal authentication database that is only limited accessible.

(2) If (1) was successful, it creates a user in Firebase’s realtime database that is accessible.

To create a user in Firebase’s realtime database, it uses the previously created reference from the Firebase class by providing the identifier (uid) of the user from Firebase’s authentication database. Then the set() method can be used to provide data for this entity which is allocated for “users/uid”. Finally, you can use the username as well to provide additional information about your user.

Note: It is fine to store user information in your own database. However, you should make sure not to store the password or any other sensible data of the user on your own. Firebase already deals with the authentication, so there is no need to store the password in your database. Many steps are necessary to secure sensible data (e.g. encryption), and it could be a security risk to perform it on your own.

After the second Firebase request that creates the user resolves successfully, the previous business logic takes place again: reset the local state and redirect to the home page. To verify the user creation is working, retrieve all the users from the realtime database in one of your other components. The admin page may be a good choice for it, because it can be used by admin users to manage the application-wide users later. First, make the admin page available via your Navigation component:

We are using the users reference from our Firebase class to attach a listener. The listener is called on(), which receives a type and a callback function. The on() method registers a continuous listener that triggers every time something has changed, the once() method registers a listener that would be called only once. In this scenario, we are interested to keep the latest list of users though.

Since the users are objects rather than lists when they are retrieved from the Firebase database, you have to restructure them as lists (arrays), which makes it easier to display them later:

You have gained full control of your users now. It is possible to create and retrieve users from your realtime database. You can decide whether this is a one-time call to the Firebase realtime database, or if you want to continuously listen for updates as well.

Exercises:

What's next?

Everything essential is in place for Firebase authentication and Firebase realtime database for user management. I am interested in seeing what you will build on top of it! If you want to continue to follow this tutorial, subscribe to my Newsletter where I will announce the launch of the complete Firebase in React book.

What’s else will be included in the book?

Role-based Authorization: So far, you have only authorized your application on a broad level, by checking for an authenticated user. In the book, you will learn how to assign roles to your users and how to give them additional privileges.

User Management: In order to get more control over your users, I will show you how to merge authentication user and database user. Then you can always assign new properties to your database user while having access to it on your user after authentication too.

Users and Messages: Next to the user management, you will introduce a second entity for messages to your application. By using both entities, user and message, we can build a chat application.

Read and Write Operations: In the application, you created a user and display a list of users with real-time capabilities. The book continuous with the usual delete and update operations to organize your users in the realtime database.

Firebase Deployment: The final step in the book is to deploy an application with Firebase. The books walks you through the process step-by-step to see your project online.

Firestore: Firebase’s Firestore is the new Firebase Realtime Database. In the book, I may show you a way to migrate to this new tech stack. Then it is up to you whether you want to use Firestore or Firebase’s Realtime Database.

Source Code Projects: This application is only build with React and Firebase. But what about taking it on the next level to enable it for real businesses? That’s where I want to show you how to migrate the project to Redux, MobX, or Gatsby.js. You will get access to variations of this application that will have additional tech:

Gatsby + Firebase

React + Redux + Firebase

React + MobX + Firebase

React + Semantic UI + Firebase

Why should I consider Redux or MobX?

As you have seen, Redux and MobX work great with React. To extend your knowledge, check out my other course Taming the State in React to learn more about Redux or MobX to use one of them in this project.

If you want to learn more about React with Firebase, grab The Road to React with Firebase to continue learning. Hopefully, this tutorial has helped you to implement your own authentication and authorization mechanism in React with Firebase. If you have any suggestions or improvements, please reach out to me. Again, if the tutorial didn’t explain everything in detail, then it was because most those things are already covered in my (partly free) courses and the other referenced articles. The tutorial builds on top of these learning materials, so make sure to check them out to learn everything about React, Redux, and MobX.