DEV Community

Cover image for Handling user authentication with Firebase in your React apps
Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

Handling user authentication with Firebase in your React apps

Written by Yusuff Faruq✏️

Sometimes, while building an application, we want each user to have a personalized experience. This is made possible through user authentication, whereby a user logs in to the application to access personalized information such as a social media feed or use a personalized service.

In this article, we will which is a backend-as-a-service (BaaS) that allows both web and mobile developers to perform common backend tasks with no need for maintenance or upkeep.

At the end of this article, we will have built a simple React app that logs users in and displays their username, email, and profile picture. You can find a demo of the application here.

Prerequisites

This article is primarily targeted towards intermediate-level React developers. However, no matter your skill level, you can still follow along well if you have an understanding of routing, the Context API, and other concepts that will be used in this article.

We will be using Tailwind CSS to style our components. Tailwind CSS is a utility-first CSS framework for rapidly building custom designs. It is very similar to Bootstrap, so it’s easy to pick up. You do not need to have a solid understanding of Tailwind CSS to follow along with this article.

LogRocket Free Trial Banner

Setting up

Before we begin coding, we need to set up some things for our app to work. Firstly, we need to create a new React app. You can easily do that using create-react-app:

npx create-react-app firebase-auth-app
Enter fullscreen mode Exit fullscreen mode

Now that we have done that, we need to set up Tailwind CSS to work with React. Here’s a great guide that helps you get Tailwind CSS up and running with create-react-app in minutes.

Next, we need to install Firebase:

npm install --save firebase
Enter fullscreen mode Exit fullscreen mode

With that done, we can now set up Firebase.

Setting up Firebase

Firstly, we need to create a new file dedicated to Firebase. Create a src folder in the root directory, and in it, create a file called firebase.js.

Now, go to the Firebase website and click on the Get started button. You will be taken to a page where you can create a new project. Once you are done, you should be taken to a dashboard page similar to the image below.

The Firebase Get Started Page

We will be using two Firebase services for this project: the Authentication service and Cloud Firestore service. Let’s set up Cloud Firestore first.

Select the Database option from the side menu. You should now be able to choose either of the two database services that Firebase provides: Cloud Firestore or the Realtime Database. In this article, we will use Cloud Firestore.

Cloud Firestore Page

Now create a Cloud Firestore database. You should now see a modal for setting the security rules of your database. Choose Start in test mode. You should have an empty database ready for use. It should look like this:

Empty Database Page

Now that we have our database ready, let’s set up authentication. From the side menu, select the Authentication option. Now, select the Sign-in method tab. You should have something like this:

Sign-in Method Page

Here, you can set up authentication for different providers such as Google, Facebook, GitHub, and so on. In our app, we want to enable Google authentication and email/password authentication. Let’s set up Google authentication first. Click on the Google option.

Google Authentication Setup Page

You can now enable Google authentication by toggling the button at the upper right-hand side. You also need to provide a project support email. Once that is done, save the changes and do the same with the email/password option.

Now that Cloud Firestore and authentication have been set up, we need to get our project’s configuration details. This is needed to link our code to our Firebase project.

To get our Firebase configuration details, go back to the project overview page and add a web app to the Firebase project. After registering the app, you should get the configuration details in the form of a JavaScript object.

const firebaseConfig = {
  apiKey: 'AIzaXXXXXXXXXXXXXXXXXXXXXXX',
  authDomain: 'test-XXXX.firebaseapp.com',
  databaseURL: 'https://test-XXXXXX.firebaseio.com',
  projectId: 'test-XXXX',
  storageBucket: 'test-XXXX.appspot.com',
  messagingSenderId: 'XXXXXXX',
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
Enter fullscreen mode Exit fullscreen mode

Now open your firebase.js file and import Firebase, Cloud Firestore, and Firebase’s Authentication service:

import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

const firebaseConfig = {
  apiKey: 'AIzaXXXXXXXXXXXXXXXXXXXXXXX',
  authDomain: 'test-XXXX.firebaseapp.com',
  databaseURL: 'https://test-XXXXXX.firebaseio.com',
  projectId: 'test-XXXX',
  storageBucket: 'test-XXXX.appspot.com',
  messagingSenderId: 'XXXXXXX',
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
Enter fullscreen mode Exit fullscreen mode

Now we just need to initialize our Firebase app using the configuration details we got from Firebase. Once that is done, we need to get references to Cloud Firestore and the Authentication service and export them.

firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
export const firestore = firebase.firestore();
Enter fullscreen mode Exit fullscreen mode

Building our components

Now that Firebase is fully set up, we can start building our components. Our application will cover:

  • Signing in and signing out
  • Signing up with Google or email/password
  • Password reset
  • A profile page

Therefore, we will build five components:

  • An Application component, which will render either the sign-in/sign-up routes or the profile page, depending on whether the user has been signed into the application
  • A PasswordReset component, which allows the user to reset their password in the event they lose or forget it
  • A ProfilePage component, which will display the user’s display name, email, and profile picture or a placeholder image if they are signing in via email/password
  • A SignIn component for logging the user in to the application
  • A SignUp component, which allows new users to sign up to use the application

Reach router will be used to route between the sign-in and sign-up routes or pages. Reach router is an accessibility-focused routing library for React. It is very easy to get started with and is a perfect fit for our application.

Now that we know what we want to do, let’s start writing some code!

In your src folder, create a new folder called Components. This is where our components will reside. Let’s start with the Application component.

The Application renders the ProfilePage component if the user has logged in and a Router component (which comes from Reach Router) housing the SignUp, SignIn, and PasswordReset components/routes otherwise.

Later on, we will use React’s Context API to pass the current user to all the components that need it. For now, we will just create a placeholder variable to represent the current user.

In case you are not familiar with Reach Router, the Router component is used to house the different routes we want to route between. Each immediate child of the Router component must have a path prop, which acts as a link to that route.

import React from "react";
import { Router } from "@reach/router";
import SignIn from "./SignIn";
import SignUp from "./SignUp";
import ProfilePage from "./ProfilePage";
import PasswordReset from "./PasswordReset";
function Application() {
  const user = null;
  return (
        user ?
        <ProfilePage />
      :
        <Router>
          <SignUp path="signUp" />
          <SignIn path="/" />
          <PasswordReset path = "passwordReset" />
        </Router>

  );
}
export default Application;
Enter fullscreen mode Exit fullscreen mode

Now, let’s build our SignIn component. The code for that is as follows:

import React, {useState} from "react";
import { Link } from "@reach/router";

const SignIn = () => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [error, setError] = useState(null);
    const signInWithEmailAndPasswordHandler = 
            (event,email, password) => {
                event.preventDefault();
    };

      const onChangeHandler = (event) => {
          const {name, value} = event.currentTarget;

          if(name === 'userEmail') {
              setEmail(value);
          }
          else if(name === 'userPassword'){
            setPassword(value);
          }
      };

  return (
    <div className="mt-8">
      <h1 className="text-3xl mb-2 text-center font-bold">Sign In</h1>
      <div className="border border-blue-400 mx-auto w-11/12 md:w-2/4 rounded py-8 px-4 md:px-8">
        {error !== null && <div className = "py-4 bg-red-600 w-full text-white text-center mb-3">{error}</div>}
        <form className="">
          <label htmlFor="userEmail" className="block">
            Email:
          </label>
          <input
            type="email"
            className="my-1 p-1 w-full"
            name="userEmail"
            value = {email}
            placeholder="E.g: faruq123@gmail.com"
            id="userEmail"
            onChange = {(event) => onChangeHandler(event)}
          />
          <label htmlFor="userPassword" className="block">
            Password:
          </label>
          <input
            type="password"
            className="mt-1 mb-3 p-1 w-full"
            name="userPassword"
            value = {password}
            placeholder="Your Password"
            id="userPassword"
            onChange = {(event) => onChangeHandler(event)}
          />
          <button className="bg-green-400 hover:bg-green-500 w-full py-2 text-white" onClick = {(event) => {signInWithEmailAndPasswordHandler(event, email, password)}}>
            Sign in
          </button>
        </form>
        <p className="text-center my-3">or</p>
        <button
          className="bg-red-500 hover:bg-red-600 w-full py-2 text-white">
          Sign in with Google
        </button>
        <p className="text-center my-3">
          Don't have an account?{" "}
          <Link to="signUp" className="text-blue-500 hover:text-blue-600">
            Sign up here
          </Link>{" "}
          <br />{" "}
          <Link to = "passwordReset" className="text-blue-500 hover:text-blue-600">
            Forgot Password?
          </Link>
        </p>
      </div>
    </div>
  );
};
export default SignIn;
Enter fullscreen mode Exit fullscreen mode

As you can see, our SignIn component has three pieces of state: email, for storing the user’s email address; password, for storing the user’s password; and error, for displaying error messages in the event of an error during the sign-in process.

Our SignIn component also uses the Link component that Reach Router provides. This component is very similar to the anchor element in HTML, and the to prop of the Link component is similar in function to the href attribute of the anchor element.

Your SignIn component should look like this:

Our SignIn Component

Next, let’s build our SignUp component. The SignUp component is very similar to the SignIn component, and the code is as follows:

import React, { useState } from "react";
import { Link } from "@reach/router";
const SignUp = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [displayName, setDisplayName] = useState("");
  const [error, setError] = useState(null);
  const createUserWithEmailAndPasswordHandler = (event, email, password) => {
    event.preventDefault();
    setEmail("");
    setPassword("");
    setDisplayName("");
  };
  const onChangeHandler = event => {
    const { name, value } = event.currentTarget;
    if (name === "userEmail") {
      setEmail(value);
    } else if (name === "userPassword") {
      setPassword(value);
    } else if (name === "displayName") {
      setDisplayName(value);
    }
  };
  return (
    <div className="mt-8">
      <h1 className="text-3xl mb-2 text-center font-bold">Sign Up</h1>
      <div className="border border-blue-400 mx-auto w-11/12 md:w-2/4 rounded py-8 px-4 md:px-8">
        {error !== null && (
          <div className="py-4 bg-red-600 w-full text-white text-center mb-3">
            {error}
          </div>
        )}
        <form className="">
          <label htmlFor="displayName" className="block">
            Display Name:
          </label>
          <input
            type="text"
            className="my-1 p-1 w-full "
            name="displayName"
            value={displayName}
            placeholder="E.g: Faruq"
            id="displayName"
            onChange={event => onChangeHandler(event)}
          />
          <label htmlFor="userEmail" className="block">
            Email:
          </label>
          <input
            type="email"
            className="my-1 p-1 w-full"
            name="userEmail"
            value={email}
            placeholder="E.g: faruq123@gmail.com"
            id="userEmail"
            onChange={event => onChangeHandler(event)}
          />
          <label htmlFor="userPassword" className="block">
            Password:
          </label>
          <input
            type="password"
            className="mt-1 mb-3 p-1 w-full"
            name="userPassword"
            value={password}
            placeholder="Your Password"
            id="userPassword"
            onChange={event => onChangeHandler(event)}
          />
          <button
            className="bg-green-400 hover:bg-green-500 w-full py-2 text-white"
            onClick={event => {
              createUserWithEmailAndPasswordHandler(event, email, password);
            }}
          >
            Sign up
          </button>
        </form>
        <p className="text-center my-3">or</p>
        <button
          className="bg-red-500 hover:bg-red-600 w-full py-2 text-white"
        >
          Sign In with Google
        </button>
        <p className="text-center my-3">
          Already have an account?{" "}
          <Link to="/" className="text-blue-500 hover:text-blue-600">
            Sign in here
          </Link>
        </p>
      </div>
    </div>
  );
};
export default SignUp;
Enter fullscreen mode Exit fullscreen mode

Our SignUp component should look like this:

Our SignUp Component

Now let’s build our ProfilePage component. We will use placeholder values for the profile picture, email, and display name. The code for the ProfilePage should look like this:

import React from "react";

const ProfilePage = () => {
  return (
    <div className = "mx-auto w-11/12 md:w-2/4 py-8 px-4 md:px-8">
      <div className="flex border flex-col items-center md:flex-row md:items-start border-blue-400 px-3 py-4">
        <div
          style={{
            background:
                `url(https://res.cloudinary.com/dqcsk8rsc/image/upload/v1577268053/avatar-1-bitmoji_upgwhc.png)  no-repeat center center`,
            backgroundSize: "cover",
            height: "200px",
            width: "200px"
          }}
          className="border border-blue-300"
        ></div>
        <div className = "md:pl-4">
        <h2 className = "text-2xl font-semibold">Faruq</h2>
        <h3 className = "italic">faruq123@gmail.com</h3>
        </div>
      </div>
      <button className = "w-full py-3 bg-red-600 mt-4 text-white">Sign out</button>
    </div>
  ) 
};
export default ProfilePage;
Enter fullscreen mode Exit fullscreen mode

With that done, let’s move to the PasswordReset component. This is where the user can input their email into a form and have a password reset email sent to them in case they lose their password. The code for this component is as follows:

import React, { useState } from "react";
import { Link } from "@reach/router";

const PasswordReset = () => {
  const [email, setEmail] = useState("");
  const [emailHasBeenSent, setEmailHasBeenSent] = useState(false);
  const [error, setError] = useState(null);
  const onChangeHandler = event => {
    const { name, value } = event.currentTarget;
    if (name === "userEmail") {
      setEmail(value);
    }
  };
  const sendResetEmail = event => {
    event.preventDefault();
  };
  return (
    <div className="mt-8">
      <h1 className="text-xl text-center font-bold mb-3">
        Reset your Password
      </h1>
      <div className="border border-blue-300 mx-auto w-11/12 md:w-2/4 rounded py-8 px-4 md:px-8">
        <form action="">
          {emailHasBeenSent && (
            <div className="py-3 bg-green-400 w-full text-white text-center mb-3">
              An email has been sent to you!
            </div>
          )}
          {error !== null && (
            <div className="py-3 bg-red-600 w-full text-white text-center mb-3">
              {error}
            </div>
          )}
          <label htmlFor="userEmail" className="w-full block">
            Email:
          </label>
          <input
            type="email"
            name="userEmail"
            id="userEmail"
            value={email}
            placeholder="Input your email"
            onChange={onChangeHandler}
            className="mb-3 w-full px-1 py-2"
          />
          <button
            className="w-full bg-blue-400 text-white py-3"
          >
            Send me a reset link
          </button>
        </form>
        <Link
         to ="/"
          className="my-2 text-blue-700 hover:text-blue-800 text-center block"
        >
          &larr; back to sign in page
        </Link>
      </div>
    </div>
  );
};
export default PasswordReset;
Enter fullscreen mode Exit fullscreen mode

Passing down the current user with the Context API

Like I pointed out earlier, we will pass down the current user to all the components that need it using React’s Context API.

The Context API allows us to pass data down a tree of components without pass that data as props of intermediate components. Therefore, regardless of how deeply nested in a component tree a component may be, it will have easy access to whatever data is stored in the context. You can learn more about Context here.

To pass down the data from Context, we will use a provider component that will house the Application component in our App.js file.

You can create a providers folder in your src folder. Inside the providers folder, create a new file called UserProvider.jsx. Import createContext alongside React. We also need to import auth from our firebase.js file. With that done, let’s create and export our context, which we will call UserContext. Its initial value will be an object with a property called user, whose value is null.

export const UserContext = createContext({ user: null });
Enter fullscreen mode Exit fullscreen mode

Now let’s create the actual UserProvider component. We need to add a piece of state in the UserProvider component called user. This piece of state will later be passed down to the other components as the value of the UserContext context.

We also need to perform some operations as soon as the UserProvider component mounts. Firebase gives us an observer called onAuthStateChanged, which we can set on the auth object to listen for changes in the state of the current user (when the user logs in and logs out).

We can also get the current user and some information about that user, such as uid, email, display name, and so on, with onAuthStateChanged. What we want to do is get the current user and set it as the value of the user state. In this case, our current user is userAuth.

componentDidMount = () => {
    auth.onAuthStateChanged(userAuth => {
      this.setState({ user: userAuth});
    });
};
Enter fullscreen mode Exit fullscreen mode

Our UserProvider component can now render the provider component that comes with our UserContext and, in turn, house the child components passed to it. Our UserProvider component should look like this:

import React, { Component, createContext } from "react";
import { auth } from "../firebase";

export const UserContext = createContext({ user: null });
class UserProvider extends Component {
  state = {
    user: null
  };

  componentDidMount = () => {
    auth.onAuthStateChanged(userAuth => {
      this.setState({ user: userAuth});
    });
  };
  render() {
    return (
      <UserContext.Provider value={this.state.user}>
        {this.props.children}
      </UserContext.Provider>
    );
  }
}
export default UserProvider;
Enter fullscreen mode Exit fullscreen mode

Once we have our UserProvider ready, we will use it to wrap our Application component in our App.js file.

import React from "react";
import Application from "./Components/Application";
import UserProvider from "./providers/UserProvider";
function App() {
  return (
    <UserProvider>
      <Application />
    </UserProvider>
  );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

Implementing Google sign-in

Implementing Google sign-in with Firebase is simple. Let’s go to our firebase.js file and create a variable called provider, whose value will be an instance of the Google provider object.

const provider = new firebase.auth.GoogleAuthProvider();
Enter fullscreen mode Exit fullscreen mode

Firebase Auth provides different methods of signing in, such as signInWithRedirect, which redirects the user to a new page, and signInWithPopup, which makes use of a pop-up. After choosing our sign-in method, we can now write and export our simple Google sign-in function:

export const signInWithGoogle = () => {
  auth.signInWithPopup(provider);
};
Enter fullscreen mode Exit fullscreen mode

We can now use this SignInWithGoogle function in the onClick handler functions for the Sign In with Google button in our SignIn and SignUp components.

Now, when you sign in with your Google account, you should be signed in to the application and taken to your profile page where you should see your display name, email, and display picture. However, because we are still using the placeholder values in the ProfilePage, this is not so. Also, the Sign out button doesn’t work so we need to modify our ProfilePage, like so:

import React, { useContext } from "react";
import { UserContext } from "../providers/UserProvider";
import {auth} from "../firebase";
const ProfilePage = () => {
  const user = useContext(UserContext);
  const {photoURL, displayName, email} = user;
  return (
    <div className = "mx-auto w-11/12 md:w-2/4 py-8 px-4 md:px-8">
      <div className="flex border flex-col items-center md:flex-row md:items-start border-blue-400 px-3 py-4">
        <div
          style={{
            background: `url(${photoURL || 'https://res.cloudinary.com/dqcsk8rsc/image/upload/v1577268053/avatar-1-bitmoji_upgwhc.png'})  no-repeat center center`,
            backgroundSize: "cover",
            height: "200px",
            width: "200px"
          }}
          className="border border-blue-300"
        ></div>
        <div className = "md:pl-4">
        <h2 className = "text-2xl font-semibold">{displayName}</h2>
        <h3 className = "italic">{email}</h3>
        </div>
      </div>
      <button className = "w-full py-3 bg-red-600 mt-4 text-white" onClick = {() => {auth.signOut()}}>Sign out</button>
    </div>
  ) 
};
export default ProfilePage;
Enter fullscreen mode Exit fullscreen mode

As you can see, we used the useContext Hook to get the current value of UserContext and grabbed the necessary data from it. We also added an onClick handler to our sign-out button. This function uses the signOut method provided by the auth object.

Now you should be able to see your correct details on your profile page.

Implementing email/password sign-in

When we implement email/password authentication, we cannot add additional data such as display name on user creation. As a solution, we will save and retrieve each user’s data on Cloud Firestore. This article will not discuss Cloud Firestore in detail but will cover the parts of Cloud Firestore we need to understand. You can visit the docs to learn more about Firestore.

So what is Cloud Firestore, and how does it store data? Well, according to the docs:

Cloud Firestore is a NoSQL, document-oriented database. Unlike a SQL database, there are no tables or rows. Instead, you store data in documents, which are organized into collections.

Each document contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents.

So, for our application, we will create a collection called users, which will contain documents for each user. These documents will contain information about the user, such as display name, email, and photo URL. We will also create an async function called generateUserDocument, which will then return the user data with the help of another function called getUserDocument.

Now, let’s write our generateUserDocument function in our firebase.js file. First of all, we need to get a reference to the user’s document in the users collection. We can do this with the doc method that Firestore provides. Once that’s done, we need to get the current content of the document, and we can do this with the get method of the user reference.

const userRef = firestore.doc(`users/${user.uid}`);
const snapshot = await userRef.get();
Enter fullscreen mode Exit fullscreen mode

We now want to check if there is data at the specified reference. If there is no data, we want to write some data to that document. After that, we will return the user’s data using the getUserDocument function. If there is data, we will return the user’s data right away.

export const generateUserDocument = async (user, additionalData) => {
  if (!user) return;
  const userRef = firestore.doc(`users/${user.uid}`);
  const snapshot = await userRef.get();
  if (!snapshot.exists) {
    const { email, displayName, photoURL } = user;
    try {
      await userRef.set({
        displayName,
        email,
        photoURL,
        ...additionalData
      });
    } catch (error) {
      console.error("Error creating user document", error);
    }
  }
  return getUserDocument(user.uid);
};
const getUserDocument = async uid => {
  if (!uid) return null;
  try {
    const userDocument = await firestore.doc(`users/${uid}`).get();
    return {
      uid,
      ...userDocument.data()
    };
  } catch (error) {
    console.error("Error fetching user", error);
  }
};
Enter fullscreen mode Exit fullscreen mode

Now that we have a function to host our users’ data on Firestore, let’s use that function in the createUserWithEmailAndPasswordHandler in our SignUp component. We can create a new user with email and password using the createUserWithEmailAndPassword method provided by Firebase, and then generate a user document for the new user with our new function, like so:

const createUserWithEmailAndPasswordHandler = async (event, email, password) => {
    event.preventDefault();
    try{
      const {user} = await auth.createUserWithEmailAndPassword(email, password);
      generateUserDocument(user, {displayName});
    }
    catch(error){
      setError('Error Signing up with email and password');
    }

    setEmail("");
    setPassword("");
    setDisplayName("");
  };
Enter fullscreen mode Exit fullscreen mode

Next, let us modify the event handler for signing in using email and password in the SignIn component. We can sign in an already registered user using the signInWithEmailAndPassword method of the Firebase Auth object.

const signInWithEmailAndPasswordHandler = (event, email, password) => {
    event.preventDefault();
    auth.signInWithEmailAndPassword(email, password).catch(error => {
      setError("Error signing in with password and email!");
      console.error("Error signing in with password and email", error);
    });
  };
Enter fullscreen mode Exit fullscreen mode

The final thing to do in order to fully implement the email/password sign-in is to modify the componentDidMount method of our UserProvider component. Since we are no longer retrieving data directly from the user object provided by the onAuthStateChanged method, we need to modify our code like so:

componentDidMount = async () => {
    auth.onAuthStateChanged(async userAuth => {
      const user = await generateUserDocument(userAuth);
      this.setState({ user });
    });
  };
Enter fullscreen mode Exit fullscreen mode

Now, new users should be able to create an account in our application using their email and password.

Implementing the password reset feature

It is a good idea to provide a password reset feature so users can reset their passwords in the event they lose them.

Firebase makes this very easy to do with a handy function called sendPasswordResetEmail, which takes in an email as an argument and sends a reset link to the email if the user has an account with our application. If the user does not have an account with our app, this function throws an error. Let’s edit the sendResetEmail function in our PasswordReset component as follows:

const sendResetEmail = event => {
    event.preventDefault();
    auth
      .sendPasswordResetEmail(email)
      .then(() => {
        setEmailHasBeenSent(true);
        setTimeout(() => {setEmailHasBeenSent(false)}, 3000);
      })
      .catch(() => {
        setError("Error resetting password");
      });
  };
Enter fullscreen mode Exit fullscreen mode

Now, registered users should be able to reset their passwords using the PasswordReset component.

Conclusion

Finally, we have come to the end of this article!

You should now be able to authenticate users in your React apps using Firebase. Firebase also supports other authentication providers such as GitHub, Twitter, and so on, and you can learn more about authentication in Firebase here.

You can also find the app we built live here.


Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

Alt Text

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — start monitoring for free.


The post Handling user authentication with Firebase in your React apps appeared first on LogRocket Blog.

Top comments (0)