DEV Community

loading...
Cover image for Project 50 of 100 - Firebase Sign Up and Login with React Router

Project 50 of 100 - Firebase Sign Up and Login with React Router

jwhubert91 profile image James Hubert ・4 min read

Hey! I'm on a mission to make 100 React.js projects ending March 31st. Please follow my dev.to profile or my twitter for updates and feel free to reach out if you have questions. Thanks for your support!

Link to the deployed project: Link
Link to the repo: github

Today I made an authentication flow using React, React Router, and Firebase. I adapted the structure from an earlier project for something new with a new UI here, but the main principles are the same. This will just be a short post highlighting the main functionalities here and how they're implemented.

We just have four components- the App component, SignUp, Login and GlobalNavbar. I have other components in the repo but they're not being used so feel free to ignore them. All of the magic happens in the App component where we import react-router-dom for our routing functionality.

# App.js

import React,{useState} from 'react';
import {auth} from './firebase';
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Link
} from 'react-router-dom';

import SignUp from './pages/SignUp';
import SignIn from './pages/SignIn';
import GlobalNavbar from './components/GlobalNavbar';
Enter fullscreen mode Exit fullscreen mode

As you can see we also import a custom {auth} object that we created in a local firebase file. That's just a js file we store in the src folder that imports the relevant firebase node modules and initializes them, and their connection to Firebase:

# firebase.js

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

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID,
  measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();

export const auth = firebase.auth();
export default firebase;
Enter fullscreen mode Exit fullscreen mode

As you can see I am storing that sensitive Firebase API information in environment variables. The node package nodenv allows us to create environment variables that can easily be left out of git commits by creating a .env file in the root of the project folder and putting our React App variables there with the following format:

REACT_APP_API_KEY=123123123123
Enter fullscreen mode Exit fullscreen mode

You can then access those variables (after a server restart) by calling process.env.REACT_APP_API_KEY in your src folder files. Make sure those variables start with REACT_APP_ or CRA won't pick them up.

Anyway, the firebase.js file above initializes the connection to Firebase and imports the relevant methods for Firebase auth, analytics, and firestore. We export firebase.auth() just for convenience and brevity.

I trust you know how to make a form in React using text inputs- so I won't go over those. You just need an email and password text inputs plus a button to make this work. I'll just go over the Firebase methods used here:

To sign up a user with an email simply use firebase.auth().createUserWithEmailAndPassword(email,password) where email and password are text strings. I do this in the following function (after some basic validation):

  const handleSignUp = () => {
    if (handleConfirmPassword()) {
      // password and confirm password match!
      auth.createUserWithEmailAndPassword(email,password)
        .then(result => {
          alert(email+' signed in successfully',clearFields());
        })
        .catch(function(error) {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        if (errorCode === 'auth/weak-password') {
          alert('The password is too weak.');
        } else {
          alert(errorMessage);
        }
        console.log(error);
      });
      clearFields()
    }
  }
Enter fullscreen mode Exit fullscreen mode

This function will alert the user whether or not the submission was successful and tell the user why if there was an error.

In the SignIn page we have a similar setup. A simple form that takes an email and password. For that the functionality is very similar and we use the firebase.auth().ignInWithEmailAndPassword(email, password) method like so:

  const logUserIn = () => {
    auth.signInWithEmailAndPassword(email, password)
      .then(result => {
        alert(email+' signed in successfully',clearFields());
      })
      .catch(function(error) {
      // Handle Errors here.
      const errorCode = error.code;
      const errorMessage = error.message;
      if (errorCode === 'auth/weak-password') {
        alert('The password is too weak.');
      } else {
        alert(errorMessage);
      }
      console.log(error);
    })
  }
Enter fullscreen mode Exit fullscreen mode

These two methods are the heart are sign in and sign up with Firebase, which takes a lot of pain out of your authentication flow.

After we've imported the pages into App.js we put them into a React Router Switch like so (with the GlobalNavbar component on top of everything so it is present regardless of the page we're on):

  return (
    <div className="App">
      <Router>
        <GlobalNavbar />
        <Switch>
          <Route path='/login'>
            <SignIn />
          </Route>
          <Route>
            <SignUp path='/' />
          </Route>
        </Switch>
      </Router>
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

I haven't done anything with it yet in this application, but the Firebase method to check if there is a logged in user or not is the following:

const [userExists,setUserExists] = useState(false);

auth.onAuthStateChanged((user) => {
    if (user) {
      setUserExists(true);
      console.log('Signed in as '+user.email);
    } else {
      setUserExists(false);
    }
  });
Enter fullscreen mode Exit fullscreen mode

If you get creative you can imagine using that piece of userExists state to automatically route a user to a main dashboard or other authenticated page if they're logged in.

Lastly, I just want to tell you about what you need to do to make an app like this work on Netlify. This app really relies on React Router working, but React Router and Netlify don't necessarily play well together. In fact, if you just upload a project with React Router to Netlify it won't work, and when you try to follow a redirect Netlify will show you a "Page does not exist" error.

So, to deal with this, before we build the project we've got to add a file called _redirects to the public folder. This tells Netlify that any redirects will come back to the index.html page that is the root of your project.

I followed this and this to get it going. Ultimately- it's just a matter of putting the following single line of code into that _redirects file:

/* /index.html 200
Enter fullscreen mode Exit fullscreen mode

That's it! Sorry it's not as detailed today- but check the code in the repo and I'm sure you can follow along. As usual if you get stuck don't be afraid to ping me in the comments :)

Discussion (9)

pic
Editor guide
Collapse
youssefrabeiii profile image
Youssef Rabei

Hey James great post, i loved it but if you want your code to be highlighted "Colored" like this

const [userExists,setUserExists] = useState(false);

auth.onAuthStateChanged((user) => {
    if (user) {
      setUserExists(true);
      console.log('Signed in as '+user.email);
    } else {
      setUserExists(false);
    }
  });
Enter fullscreen mode Exit fullscreen mode

add the language like javascript or jsx "I used jsx for the code above" after the three "backticks" (No spaces between them)

and just a note it would be better if you displayed a paragraph when there is an error not an alert so you can style it with CSS

Collapse
ridhikgovind profile image
Ridhik Govind

Nice tip. Gonna implement it next time in my next article. Thanks :)

Collapse
jwhubert91 profile image
James Hubert Author

Hey! How'd you do this Youssef? This is really great.

Collapse
youssefrabeiii profile image
Youssef Rabei

Everytime I try to show it it automatically make it code but you should put the language name after the three backticks (the three things that look like a comma) and it will highlight it automatically

Thread Thread
jwhubert91 profile image
James Hubert Author

Oh got it. Like

some code here...
Enter fullscreen mode Exit fullscreen mode
Thread Thread
jwhubert91 profile image
James Hubert Author

Nice!

const thankful = true;

if (thankful) {
  console.log("Thanks Youssef!");
}
Enter fullscreen mode Exit fullscreen mode
Collapse
imsikun profile image
Subhakant Mishra

Man this is gem.
Thanks just been looking for this.

Collapse
jwhubert91 profile image
James Hubert Author

Thank you very much Subhakant! Let me know what you build with it :)

Collapse
imsikun profile image
Subhakant Mishra

Sure
Will come back soon