User registration and login with React and Axios
Tags: javascript, webdev, react, axios
Published: 2022-05-21 22:37:15

by [Marvel Ken](https://blog.openreplay.com/authors/marvel-ken)


Most web applications nowadays require user authentication and authorization: one for validation and confirming that users are who they say they are, and the other for granting permissions and access to recognized users. A popular option is using a social login provider like Google or Facebook. Uniquely, we can build authentication with React, Axios, and a Node.js backend.

In this article, we will create a React login form that uses Axios to submit users' information for authentication with key accessibilities to deliver success. This article is both beginner- and intermediate-level developer-friendly.

## Prerequisites

For this tutorial, we will be focusing only on the frontend aspect of things, but here is a [](https://github.com/marvelken/Node.git)[link](https://github.com/marvelken/Node.git) to the GitHub repo, where I have a backend prepared for this tutorial. Clone it, `run yarn install` or `npm install` to install all dependencies, and then run `npm run dev` or `yarn dev` to start up the server, which will be running on http://localhost:3500/. 

Next, create a `.env` file, which will store our environmental variables. Open the terminal and write Node, now, copy and paste the following into the prompt:

```javascript
require("crypto").randomBytes(64).toString("hex");
```

This will create a unique token for you. Copy the token and in the `.env` file, create an ACCESS_TOKEN_SECRET=" Unique token goes here". 
Next, create another token, and in the `.env` file, create a REFRESH_TOKEN_SECRET with the unique token.

![Setting up a backend](https://blog.openreplay.com/static/ac210b1b8fe4146e46718587dc7575ee/b8c6a/image01.png)

With that, our backend is ready.

## Creating a registration form

We will create a new React app with the command below:

```
npx create-react-app auth
```

Using yarn:

```
yarn create react-app auth
```

I will be using yarn from here on out, and you can name your application whatever you want.
Next, create a registration file named register.jsx for handling the registration and inside, go ahead, copy and paste the code below:

```javascript
const Register = () => {
  return (
    <>
      <section>
        <p
          ref={errRef}
          className={errMsg ? "errmsg" : "offscreen"}
          aria-live="assertive"
        >
          {errMsg}
        </p>
        <h1>Register</h1>
        <form onSubmit={handleSubmit}>
          <label htmlFor="username">
            Username:
            <FontAwesomeIcon
              icon={faCheck}
              className={validName ? "valid" : "hide"}
            />
            <FontAwesomeIcon
              icon={faTimes}
              className={
                validName || !user ? "hide" : "invalid"
              }
            />
          </label>
          <input
            type="text"
            id="username"
            ref={userRef}
            autoComplete="off"
            onChange={(e) => setUser(e.target.value)}
            value={user}
            required
            aria-invalid={validName ? "false" : "true"}
            aria-describedby="uidnote"
            onFocus={() => setUserFocus(true)}
            onBlur={() => setUserFocus(false)}
          />
          <p
            id="uidnote"
            className={
              userFocus && user && !validName
                ? "instructions"
                : "offscreen"
            }
          >
            <FontAwesomeIcon icon={faInfoCircle} />
            4 to 24 characters.
            <br />
            Must begin with a letter.
            <br />
            Letters, numbers, underscores, hyphens allowed.
          </p>
          <label htmlFor="password">
            Password:
            <FontAwesomeIcon
              icon={faCheck}
              className={validPwd ? "valid" : "hide"}
            />
            <FontAwesomeIcon
              icon={faTimes}
              className={
                validPwd || !pwd ? "hide" : "invalid"
              }
            />
          </label>
          <input
            type="password"
            id="password"
            onChange={(e) => setPwd(e.target.value)}
            value={pwd}
            required
            aria-invalid={validPwd ? "false" : "true"}
            aria-describedby="pwdnote"
            onFocus={() => setPwdFocus(true)}
            onBlur={() => setPwdFocus(false)}
          />
          <p
            id="pwdnote"
            className={
              pwdFocus && !validPwd
                ? "instructions"
                : "offscreen"
            }
          >
            <FontAwesomeIcon icon={faInfoCircle} />
            8 to 24 characters.
            <br />
            Must include uppercase and lowercase letters, a
            number and a special character.
            <br />
            Allowed special characters:{" "}
            <span aria-label="exclamation mark">
              !
            </span>{" "}
            <span aria-label="at symbol">@</span>{" "}
            <span aria-label="hashtag">#</span>{" "}
            <span aria-label="dollar sign">$</span>{" "}
            <span aria-label="percent">%</span>
          </p>
          <label htmlFor="confirm_pwd">
            Confirm Password:
            <FontAwesomeIcon
              icon={faCheck}
              className={
                validMatch && matchPwd ? "valid" : "hide"
              }
            />
            <FontAwesomeIcon
              icon={faTimes}
              className={
                validMatch || !matchPwd ? "hide" : "invalid"
              }
            />
          </label>
          <input
            type="password"
            id="confirm_pwd"
            onChange={(e) => setMatchPwd(e.target.value)}
            value={matchPwd}
            required
            aria-invalid={validMatch ? "false" : "true"}
            aria-describedby="confirmnote"
            onFocus={() => setMatchFocus(true)}
            onBlur={() => setMatchFocus(false)}
          />
          <p
            id="confirmnote"
            className={
              matchFocus && !validMatch
                ? "instructions"
                : "offscreen"
            }
          >
            <FontAwesomeIcon icon={faInfoCircle} />
            Must match the first password input field.
          </p>
          <button
            disabled={
              !validName || !validPwd || !validMatch
                ? true
                : false
            }
          >
            Sign Up
          </button>
        </form>
        <p>
          Already registered?
          <br />
          <span className="line">
            {/*put router link here*/}
            {/* <a href="#">Sign In</a> */}
          </span>
        </p>
      </section>
      )}
    </>
  );
};

export default Register;
```     

In the code block above, we created a form for registration, and here is why you can't authenticate users that are not registered. We created some state and ref events which we will create in the following sections. We also added Aria-labels for screen accessibility; you can read more on aria-labels [here](https://www.lullabot.com/articles/what-heck-aria-beginners-guide-aria-accessibility) and [here](https://www.lullabot.com/articles/what-heck-aria-beginners-guide-aria-accessibility).
 

## Creating and handling validation logic

We will create a regex for handling username values and one for handling password values. 

```javascript
const USER_REGEX = /^\[A-z\][A-z0-9-_]{3,23}$/;
const PWD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%]).{8,24}$/;
```

Next, we will create the state events and also the ref events. The ref events will be used to set a focus on the errors so it could display error characters if an error occurs so that the user can be notified for wrong inputs. This would be right before we return any HTML element in the component. 

```javascript
const userRef = useRef();
const errRef = useRef();
const [user, setUser] = useState("");
const [validName, setValidName] = useState(false);
const [userFocus, setUserFocus] = useState(false);
const [pwd, setPwd] = useState("");
const [validPwd, setValidPwd] = useState(false);
const [pwdFocus, setPwdFocus] = useState(false);
const [matchPwd, setMatchPwd] = useState("");
const [validMatch, setValidMatch] = useState(false);
const [matchFocus, setMatchFocus] = useState(false);
const [errMsg, setErrMsg] = useState("");
const [success, setSuccess] = useState(false);
// This will be used with the login
```
    
We will also create some `useEffects` to set the useRef focus and handle and check changes to our state events.

```javascript
// State events
useEffect(() => {
  userRef.current.focus();
}, []);

useEffect(() => {
  setValidName(USER_REGEX.test(user));
}, [user]);

useEffect(() => {
  setValidPwd(PWD_REGEX.test(pwd));
  setValidMatch(pwd === matchPwd);
}, [pwd, matchPwd]);

useEffect(() => {
  setErrMsg("");
}, [user, pwd, matchPwd]);
```

## Handling the submit function

We will install Axios, which will help send the registration data to our backend. It will be used in authenticating a user when they log in. Install Axios using the command below:

```
yarn add Axios
```

Create a folder within the src directory and name it `api/axios.js`. This folder contains a file that will be responsible for sending our registered data to our backend.

```javascript
// axios.js
import axios from "axios";
export default axios.create({
  baseURL: "http://localhost:3500",
});
```

In the code above, we created a baseURL specifying the URL where our backend will exist. We then import it into our registration file and then use it to handle submitting the filled data of registered users to the backend.

```javascript
// Register.js
const REGISTER_URL = "/register";
import axios from "./api/axios";
// other imports
const handleSubmit = async (e) => {
  e.preventDefault();
  const v1 = USER_REGEX.test(user);
  const v2 = PWD_REGEX.test(pwd);
  if (!v1 || !v2) {
    setErrMsg("Invalid Entry");
    return;
  }

  try {
    const response = await axios.post(
      REGISTER_URL,
      JSON.stringify({ user, pwd }),
      {
        headers: { "Content-Type": "application/json" },
        withCredentials: true,
      }
    );
    setSuccess(true);
    //clear state and controlled inputs
    setUser("");
    setPwd("");
    setMatchPwd("");
  } catch (err) {
    if (!err?.response) {
      setErrMsg("No Server Response");
    } else if (err.response?.status === 409) {
      setErrMsg("Username Taken");
    } else {
      setErrMsg("Registration Failed");
    }
    errRef.current.focus();
  }
};
```


We created a function for handling our submission in the code block above. Inside we checked to see if the username or password passed the regex test. We also created a registration URL that we can submit to the backend.

![The register form](https://blog.openreplay.com/static/e3adf24edec6d5ad9de39deab9b8d514/d0434/image02.png)

And, after submitting the data:

![After registering](https://blog.openreplay.com/static/40455579fb735df54202268c87c203a1/1c412/image03.png)

And with that, we are done creating the registration form. Let's create a login form to test whether we can log in with the registered credentials.


## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source alternative to FullStory and LogRocket. It gives you full observability by replaying everything your users do on your app and showing how your stack behaves for every issue. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1647097041609/Qi6sMOaaB.png)

_Happy debugging for modern frontend teams - [start monitoring your web app for free](https://github.com/openreplay/openreplay)._



## Creating a Login form

Create a file inside the src folder and name it `Login.js`. Open the file, copy the code below and paste it inside:

```javascript
// Login.js
import { useRef, useState, useEffect, useContext } from "react";
const Login = () => {
  const userRef = useRef();
  const errRef = useRef();
  const [user, setUser] = useState("");
  const [pwd, setPwd] = useState("");
  const [errMsg, setErrMsg] = useState("");
  const [success, setSuccess] = useState(false);
  useEffect(() => {
    userRef.current.focus();
  }, []);
  useEffect(() => {
    setErrMsg("");
  }, [user, pwd]);

  return (
    <>
      {success ? (
        <section>
          <h1>You are logged in!</h1>
          <br />
          <p>{/* <a href="#">Go to Home</a> */}</p>
        </section>
      ) : (
        <section>
          <p
            ref={errRef}
            className={errMsg ? "errmsg" : "offscreen"}
            aria-live="assertive"
          >
            {errMsg}
          </p>
          <h1>Sign In</h1>
          <form onSubmit={handleSubmit}>
            <label htmlFor="username">Username:</label>
            <input
              type="text"
              id="username"
              ref={userRef}
              autoComplete="off"
              onChange={(e) => setUser(e.target.value)}
              value={user}
              required
            />
            <label htmlFor="password">Password:</label>
            <input
              type="password"
              id="password"
              onChange={(e) => setPwd(e.target.value)}
              value={pwd}
              required
            />
            <button>Sign In</button>
          </form>
          <p>
            Need an Account?
            <br />
            <span className="line">
              <a href="#">Sign Up</a>
            </span>
          </p>
        </section>
      )}
    </>
  );
};

export default Login;
```

We created a login form in the code block above. We also created state and ref events to control our inputs.


## Creating AuthContext 

In this section, we will create a global Auth state and use the context API. Inside our src folder, we would create a folder called context-Api and a file right inside it called `AuthProvider.js`.

```javascript
// AuthProvider.js
import { createContext, useState } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
  const [auth, setAuth] = useState({});
  return (
    <AuthContext.Provider value={{ auth, setAuth }}>
      {children}
    </AuthContext.Provider>
  );
};

export default AuthContext;
```

In the code above, we defined our global state, which we will import into our `index.js` file and wrap our App component so that the entire app can have access to the auth state.

```javascript
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import { AuthProvider } from "./context/AuthProvider";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <AuthProvider>
      <App />
    </AuthProvider>
  </React.StrictMode>
);
```

Next, we import `AuthContext` into our `login.js`. We also created a handleSubmit function in charge of sending and verifying the data sent to the backend.

```javascript
// Login.js
// imports
import AuthContext from "./context/AuthProvider";
function Login() {
  const { setAuth } = useContext(authContext);
  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await axios.post(
        LOGIN_URL,
        JSON.stringify({ user, pwd }),
        {
          headers: { "Content-Type": "application/json" },
          withCredentials: true,
        }
      );
      const accessToken = response?.data?.accessToken;
      const roles = response?.data?.roles;
      setAuth({ user, pwd, roles, accessToken });
      setUser("");
      setPwd("");
      setSuccess(true);
    } catch (err) {
      if (!err?.response) {
        setErrMsg("No Server Response");
      } else if (err.response?.status === 400) {
        setErrMsg("Missing Username or Password");
      } else if (err.response?.status === 401) {
        setErrMsg("Unauthorized");
      } else {
        setErrMsg("Login Failed");
      }
      errRef.current.focus();
    }
  };
}
```

You can add the following styles inside your `index.js` file.

```
@import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap');
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  font-family: 'Nunito', sans-serif;
  font-size: 22px;
  color: #fff;
}

body {
  min-height: 100vh;
  background-color: rgb(255, 184, 30);
}

.App {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: 1rem 0.5rem;
}

section {
  width: 100%;
  max-width: 420px;
  min-height: 400px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding: 1rem;
  background-color: rgba(0, 0, 0, 0.4);
}

form {
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  flex-grow: 1;
  padding-bottom: 1rem;
}

a,
a:visited {
  color: #fff;
}

input[type='text'],
input[type='password'],
button,
textarea {
  font-family: 'Nunito', sans-serif;
  font-size: 22px;
  padding: 0.25rem;
  border-radius: 0.5rem;
}

label,
button {
  margin-top: 1rem;
}

button {
  padding: 0.5rem;
}

.instructions {
  font-size: 0.75rem;
  border-radius: 0.5rem;
  background: #000;
  color: #fff;
  padding: 0.25rem;
  position: relative;
  bottom: -10px;
}

.instructions>svg {
  margin-right: 0.25rem;
}

.offscreen {
  position: absolute;
  left: -9999px;
}

.hide {
  display: none;
}

.valid {
  color: limegreen;
  margin-left: 0.25rem;
}

.invalid {
  color: red;
  margin-left: 0.25rem;
}

.errmsg {
  background-color: lightpink;
  color: firebrick;
  font-weight: bold;
  padding: 0.5rem;
  margin-bottom: 0.5rem;
}

.line {
  display: inline-block;
}
``` 

This would be the result:

![The sign-in form](https://blog.openreplay.com/static/55d6f50e2ab331b4bbc59c87b49c3ef7/662a1/image04.png)

And, after clicking in the button:

![After successfully logging in](https://blog.openreplay.com/static/b191824ae0707f9a5385bfea47f068c4/15ea4/image05.png)

## Conclusion

In this article, we learned about User Login and authentication using React18  and Axios. We also learned the basic way to authenticate with a Node.js server. Here is the [git repo for the React project](https://github.com/marvelken/login-and-registration.git).

[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)