How to implement authentication in React TypeScript using JWT authentication

How to implement authentication in React TypeScript using JWT authentication
Back to blog

In this article, we would like to introduce you to the most popular and modern way of protecting or securing your React Typescript Web Application by using JWT Authentication. So, what is this security or protection, and why do we need it?

Well, we will discuss all these things in our blog. So, let’s begin:

What is Security and Protection in Software? 

In order to secure or save the data and users’ information from an unknown third-party library, XSS attacks, web scraping, or a professional hacker, you can use certain security protocols like HTTPS over HTTP, or making authentication in your application or maybe you can use any other security encryption methods or checksum methods so that your application’s data and integrity remains safe.

Why do we need security? 

Well, in order to protect your applications from certain attacks and hackers, as we have mentioned above, some reasons why we need them. Let me explain the same with an example:

Example 1: 

If you plan to go to a hill station for a picnic with the whole family, then why do you keep the doors and windows locked when you leave your house? 

Because someone can enter your house if your doors and windows are not locked, right? Thieves can take precious items; any animal can rehabilitate like a snake. And you can’t do anything because what’s gone is gone, because you didn’t have security. Right? Lock protects your house the same way as the security does in your software applications. 

Almost every company nowadays uses security or authentication, like JWT (JSON Web Token). If JWT is not implemented, a large number of users’ accounts can be hacked by any hacker.

What is JWT or JSON Web Token? 

JSON Web Token is a modern Internet standard for generating data with a voluntary signature and voluntary encryption whose payload carries JSON that declares some number of claims. The tokens have been signed either by using a private secret or a public/private key.

How to implement JWT into a React TypeScript Application (Step-by-step process) 

Steps 1: Create a React TypeScript App on your system by following command:

npx create-react-app <Any-name-you want> --template typescript

Step 2:  

Go to your React Typescript Application Directory and install Bootstrap, Axios, React Hook Form, react-toastify & react-router-dom packages using the command below: 

npm install axios bootstrap [email protected] react-hook-form react-toastify

Step 3: 

In your App.tsx file, clear all the code and paste the following. Basically, what we are trying to do here is to import all the components we will create in the future and add react-router-dom functionality for routing into the pages with a different-different links:

import React from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.bundle.min.js";
import SignUp from "./Components/SignUp";
import Login from "./Components/Login";
import PrivateRoute from "./Auth/PrivateRoute";
import Home from "./Components/Home";
import {BrowserRouter, Route, Switch} from 'react-router-dom';
function App() {
  return (
      <BrowserRouter>
      <Switch>
        <PrivateRoute exact path="/login" component={Login}/>
        <Route exact path="/register" component={SignUp} />
        </Switch>
    </BrowserRouter>
  );
}

export default App;

Step 4: 

Now we will create a private route for our application. Create a folder named “Auth” and under this folder create a new file known as a PrivateRoute.tsx file, copy all the code and paste the following. Basically, what we are trying to do here is we are taking out our JWT Token from the LocalStorage

And check if we do not have a JWT token in our LocalStorage then we will go to the Login route, else if it has a token, then it will remain on the homepage. 

import React from "react";
import { Redirect, Route } from "react-router-dom";

const PrivateRoute = (props) => {
  const token = localStorage.getItem("auth");
  return <>{token ? <Route {...props} /> : <Redirect to="/login" />}</>;
};

export default PrivateRoute;

Steps 5: 

Now we will create a UI for this. First, go to the src folder, then inside the src folder, make a new folder named “Components”. 

And inside the components folder, make 3 files as:  

  1. Login.tsx

  1. SignUp.tsx 

  1. Home.tsx 

  1. home.css 

Before we proceed further, make sure that you have your own Node API of JWT Authentication, which provides a JWT Token. Because we are using the one created on our local machine. Please change the http://localhost:4000 URL in the code according to your API URL code

In Login.tsx

import React, { FC } from "react";
import { Link } from "react-router-dom";
import { useForm } from "react-hook-form";
import axios from "axios";
import { ToastContainer, toast, Flip } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";
import { RouteComponentProps } from "react-router";

type SomeComponentProps = RouteComponentProps;
const Login: FC<SomeComponentProps> = ({ history }): JSX.Element => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const login = (data: any) => {
    let params = {
      email: data.email,
      password: data.password,
    };
    axios
      .post("http://localhost:4000/api/login", params)
      .then(function (response) {
        //   IF EMAIL ALREADY EXISTS
        if (response.data.success === false) {
          toast.error(response.data.error, {
            position: "top-right",
            autoClose: 3000,
            hideProgressBar: true,
            closeOnClick: true,
            pauseOnHover: true,
            draggable: false,
            progress: 0,
            toastId: "my_toast",
          });
        } else {
          toast.success(response.data.message, {
            position: "top-right",
            autoClose: 3000,
            hideProgressBar: true,
            closeOnClick: true,
            pauseOnHover: true,
            draggable: false,
            progress: 0,
            toastId: "my_toast",
          });
          localStorage.setItem("auth", response.data.token);
          setTimeout(() => {
            history.push("/");
          }, 3000);
        }
      })

      .catch(function (error) {
        console.log(error);
      });
  };

  return (
    <>
      <div className="container">
        <div
          className="row d-flex justify-content-center align-items-center"
          style={{ height: "100vh" }}
        >
          <div className="card mb-3" style={{ maxWidth: "320px" }}>
            <div className="col-md-12">
              <div className="card-body">
                <h3 className="card-title text-center text-secondary mt-3">
                  Login Form
                </h3>
                <form autoComplete="off" onSubmit={handleSubmit(login)}>
                  <div className="mb-3 mt-4">
                    <label className="form-label">Email</label>
                    <input
                      type="email"
                      className="form-control shadow-none"
                      id="exampleFormControlInput1"
                      {...register("email", { required: "Email is required!" })}
                    />
                    {errors.email && (
                      <p className="text-danger" style={{ fontSize: 14 }}>
                        {errors.email.message}
                      </p>
                    )}
                  </div>
                  <div className="mb-3">
                    <label className="form-label">Password</label>
                    <input
                      type="password"
                      className="form-control shadow-none"
                      id="exampleFormControlInput2"
                      {...register("password", {
                        required: "Password is required!",
                      })}
                    />
                    {errors.password && (
                      <p className="text-danger" style={{ fontSize: 14 }}>
                        {errors.password.message}
                      </p>
                    )}
                  </div>
                  <div className="text-center mt-4 ">
                    <button
                      className="btn btn-outline-primary text-center shadow-none mb-3"
                      type="submit"
                    >
                      Submit
                    </button>
                    <p className="card-text pb-2">
                      Have an Account?{" "}
                      <Link style={{ textDecoration: "none" }} to={"/register"}>
                        Sign Up
                      </Link>
                    </p>
                  </div>
                </form>
              </div>
            </div>
          </div>
        </div>
      </div>
      <ToastContainer
        position="top-right"
        autoClose={5000}
        hideProgressBar
        closeOnClick
        rtl={false}
        pauseOnFocusLoss={false}
        draggable={false}
        pauseOnHover
        limit={1}
        transition={Flip}
      />
    </>
  );
};
export default Login;

In SignUp.tsx

import React, { FC } from "react";
import { Link, RouteComponentProps } from "react-router-dom";
import { useForm } from "react-hook-form";
import axios from "axios";
import { ToastContainer, toast, Flip } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";

type SomeComponentProps = RouteComponentProps;
const SignUp: FC<SomeComponentProps> = ({ history }) => {
  const {
    register,
    handleSubmit,
    watch,
    reset,
    formState: { errors },
  } = useForm();
  const submitData = (data: any) => {
    let params = {
      firstname: data.firstname,
      lastname: data.lastname,
      email: data.email,
      password: data.password,
      confirmpassword: data.cpassword,
    };
    console.log(data);
    axios
      .post("http://localhost:4000/api/signup", params)
      .then(function (response) {
        toast.success(response.data.message, {
          position: "top-right",
          autoClose: 3000,
          hideProgressBar: true,
          closeOnClick: true,
          pauseOnHover: true,
          draggable: false,
          progress: 0,
          toastId: "my_toast",
        });
        reset();
        setTimeout(() => {
          history.push("/login");
        }, 3000);
      })

      .catch(function (error) {
        console.log(error);
      });
  };
  return (
    <>
      <div className="container">
        <div
          className="row d-flex justify-content-center align-items-center"
          style={{ height: "100vh" }}
        >
          <div className="card mb-3 mt-3 rounded" style={{ maxWidth: "500px" }}>
            <div className="col-md-12">
              <div className="card-body">
                <h3 className="card-title text-center text-secondary mt-3 mb-3">
                  Sign Up Form
                </h3>
                <form
                  className="row"
                  autoComplete="off"
                  onSubmit={handleSubmit(submitData)}
                >
                  <div className="col-md-6">
                    <div className="">
                      <label className="form-label">Firstname</label>
                      <input
                        type="text"
                        className="form-control form-control-sm"
                        id="exampleFormControlInput1"
                        {...register("firstname", {
                          required: "Firstname is required!",
                        })}
                      />
                      {errors.firstname && (
                        <p className="text-danger" style={{ fontSize: 14 }}>
                          {errors.firstname.message}
                        </p>
                      )}
                    </div>
                  </div>
                  <div className="col-md-6">
                    <div className="">
                      <label className="form-label">Lastname</label>
                      <input
                        type="text"
                        className="form-control form-control-sm"
                        id="exampleFormControlInput2"
                        {...register("lastname", {
                          required: "Lastname is required!",
                        })}
                      />
                      {errors.lastname && (
                        <p className="text-danger" style={{ fontSize: 14 }}>
                          {errors.lastname.message}
                        </p>
                      )}
                    </div>
                  </div>

                  <div className="">
                    <label className="form-label">Email</label>
                    <input
                      type="email"
                      className="form-control form-control-sm"
                      id="exampleFormControlInput3"
                      {...register("email", { required: "Email is required!" })}
                    />
                    {errors.email && (
                      <p className="text-danger" style={{ fontSize: 14 }}>
                        {errors.email.message}
                      </p>
                    )}
                  </div>
                  <div className="">
                    <label className="form-label">Password</label>
                    <input
                      type="password"
                      className="form-control form-control-sm"
                      id="exampleFormControlInput5"
                      {...register("password", {
                        required: "Password is required!",
                      })}
                    />
                    {errors.password && (
                      <p className="text-danger" style={{ fontSize: 14 }}>
                        {errors.password.message}
                      </p>
                    )}
                  </div>
                  <div>
                    <label className="form-label">Confirm Password</label>
                    <input
                      type="password"
                      className="form-control form-control-sm"
                      id="exampleFormControlInput6"
                      {...register("cpassword", {
                        required: "Confirm Password is required",

                        validate: (value) =>
                          value === watch("password") ||
                          "Passwords don't match.",
                      })}
                    />
                    {errors.cpassword && (
                      <p className="text-danger" style={{ fontSize: 14 }}>
                        {errors.cpassword.message}
                      </p>
                    )}
                  </div>
                  <div className="text-center mt-4 ">
                    <button
                      className="btn btn-outline-primary text-center shadow-none mb-3"
                      type="submit"
                    >
                      Submit
                    </button>
                    <p className="card-text">
                      Already have an account?{" "}
                      <Link style={{ textDecoration: "none" }} to={"/login"}>
                        Log In
                      </Link>
                    </p>
                  </div>
                </form>
              </div>
            </div>
          </div>
        </div>
      </div>
      <ToastContainer
        position="top-right"
        autoClose={5000}
        hideProgressBar
        closeOnClick
        rtl={false}
        pauseOnFocusLoss={false}
        draggable={false}
        pauseOnHover
        limit={1}
        transition={Flip}
      />
    </>
  );
};

export default SignUp;

In Home.tsx

import React, { FC } from "react";
import { RouteComponentProps } from "react-router-dom";
import "./home.css";

type SomeComponentProps = RouteComponentProps;
const Home: FC<SomeComponentProps> = ({ history }) => {
  const logout = () => {
    localStorage.clear();
    history.push("/login");
  };
  return (
    <>
      <div
        style={{
          display: "flex",
          justifyContent: "space-between",
          paddingLeft: 50,
          paddingRight: 50,
        }}
      >
        <div>
          <h3 className="m-3">Home</h3>
        </div>
        <div>
          <button type="submit" className="buttons" onClick={logout}>
            Logout
          </button>
        </div>
      </div>
      <div className="container">
        <div
          className="row d-flex justify-content-center align-items-center text-center"
          style={{ height: "100vh" }}
        >
          <p className="muted display-6">Hello User👋</p>
        </div>
      </div>
    </>
  );
};

export default Home;

In home.css

.buttons
{
    color: white;
    background: cornflowerblue;
    padding: 10px 40px;
    margin: 10px;
    border: none;
    border-radius: 30px;
    cursor: pointer;
}

Steps 6: 

Now execute the code by following the upcoming command:

npm start

Steps 7: 

Now you will see your output like this: 

Signup Page: 

 

Login Page: 

After entering a valid login detail, inspect the browser to see if there is a JWT token or not? If it is a valid login, then the screen will look like this; else, it will show you an incorrect password message from your API.

Now, if you notice, there is a weird auth token generated on the Application ‘Tab’ Under the LocalStorage auth area. This is signed or a private key that is required by JWT Authentication from an API that you have used. 

In case you delete this token from the browser, you will be redirected again to the Login page, as there is no token available on it, and the private route will play its part here. 

Short Snippet of Logic: 

  1. Login: In Login, you are just passing the JSON data of a user and getting a token from a RESTful API

  • Signup: In Sign Up, you are again passing the JSON data of a user and saving the user into the database. 

  • Home: In Home, you are logging in our user with the correct credential that he/she enters and sets our JWT Token to the browser’s LocalStorage, which comes from the Login API. 

  • Logout: In Logout, you clear the JWT Token from your LocalStorage

Conclusion: 

JWT Authentications are widely used nowadays; kindly use it only where it is required in your projects. Do not use them if you are just using the signup login for your personal use. Because your data is local and it will be only available to you, there is no point in using it for personal use. But yes, it’s a very powerful, robust, and secure method to transmit your data.

Code Availability: 

https://github.com/Akshay80/React-TS-login-using-jwt/tree/main/src/Auth

Thank you so much for reading this blog so patiently, 

Have a good day.

Real-World Results

Discover How We Deliver Results

blog-postPost preview
Pooja Sharma 01 Jan 1970

TypeScript Vs JavaScript Vs ECMAScript – Know The Difference

In this article, you will know the major differences between Typescript, Javascript, and Ecmascript.

blog-postPost preview
Subodh Dharmwan 01 Jan 1970

Core Data in iOS & macOS: A Complete Guide to CRUD, Predicates, and Data Management

Core Data is one of Apple's most popular frameworks for iOS and macOS apps. Core data is used to manage the model layer objects in applications.

blog-postPost preview
Pooja Sharma 01 Jan 1970

Top 9 Software Development Models to Choose From: Phases and Applications

Here are the top 9 Software Development Models to Choose From: Phases and Applications.

blog-postPost preview
Pooja Sharma 06 Jul 2025

Software Development Time Estimation: A Practice Guide

This hands-on software development time estimation guide describes the basics of estimating time, provides practical time-estimating techniques.

blog-postPost preview
Pooja Sharma 14 Aug 2025

A Quick Guide to Install Laravel 8 on Windows 10 XAMPP

Laravel 8 is a PHP-based web application framework, here is a quick guide on how to install Laravel 8 on windows 10 xampp.

Turning expertise into action for your business.

We are more than just developers and consultants—we are your partners in navigating the digital landscape. Let us be the engine behind your next big success while you focus on your core vision.

Explore Opportunities!