In this tutorial, We are going to learn how to create a modern Login and Sign Up User Interface using React.js and Tailwind CSS. We will cover the basics of setting up a React project, using Tailwind to style the page, and configuring the signup and sign-in forms using Jwt authentication.

The Tailwind CSS framework will be used to style components and React.js to create components and handle the logic.

This tutorial will be a step-by-step guide to creating a fully functional and aesthetically pleasing Login and Sign Up UI with functional authentication using Jwt.

With this knowledge, you will be able to create a powerful, secure signup and sign-in page for your own website or application.

Prerequisites

To successfully complete the tutorial, make sure you have all of the things mentioned below:

  • A configured computer with Mac OS, Windows, or Linux with an internet connection.
  • Basic knowledge of HTML, CSS, and JavaScript.
  • Code Editor -You can choose any text editor on which you may write JavaScript code such as sublime text, notepad++, atom, etc. In this tutorial, we will use visual studio code (vs code).
  • NodeJS is installed on your computer. We are using Node version v16.17.1. You may use this version or higher. But it is recommended not to use the node version under 16.0.0 while completing this tutorial.
  • NPM is installed on your computer. We are using NPM version 8.15.0.
  • Basic knowledge of Express.Js

Steps to build React Login & Sign Up UI with Jwt and tailwind CSS

  • setup react project
  • configure ‘Tailwind CSS’ in react project
  • setup routing in react.js
  • setup private routing
  • implement a login page with React and Tailwind CSS
  • implement a sign-up page with React and Tailwind CSS
  • handle form
  • authenticate users using JWT
  • build an express server
  • implement rest API
  • add cors policy
  • connect the back-end server with the front-end

Setup Our Project

To get into the main part of this tutorial, we need to set up our project. Now we will see, how to create a react project and how to configure tailwind CSS with react app. So without wasting time, let’s get started…
Follow the steps:

  • Open VS code
  • Open VS Code terminal and run the command:
npx create-react-app "react-login-signup-system"

Creating React app

After completing the process, we will see a successful message like this:

successful message

If you see the above output, Congratulations! you are ready to move on.

Now open the project with VS code and open the VS code terminal and make sure you are inside “react-login-signup-system” directory, like the image below.

  • Now we will install ‘react router dom’. To install it run the following command.
npm install react-router-dom

  • It is time to install ‘tailwind CSS’ and configure it with our project.
npm install -D tailwindcss
npx tailwindcss init

After running those command make sure a file called ‘tailwind.config.js’ has been created.

Open ‘tailwind.config.js’ file and add the following code in ‘content’.

"./src/**/*.{html,jsx, js}"

  • Open “./src/index.css” file and add the following code.
@tailwind base;
@tailwind components;
@tailwind utilities;

Wowww…  We did a great job. Now We are good to run the project. To run the project, let’s run the below command:

npm start

After running the command, the default address in the browser will be turned on and we will get an output like this.

Now our project has been set up successfully :). If you are tired, you can have tea or coffee and make yourself ready to make your beautiful hands dirty with code.

Configure Routing

To configure routing, firstly we will create the pages. We need a total of three pages, loginsign up, and home page. Firstly, we need to create a directory called “pages” inside “src”.  Now let’s create the pages inside the ‘pages’ directory, file names will be “Login.jsx”“SignUp.jsx ” and “Home.jsx”.

Now we will create react functional component:

Login page code

import React from 'react'
export default function Login() {
    return (
        <h1>this is login page</h1>
    )
}

Sign up page code

import React from 'react'
export default function SignUp() {
    return (
        <h1>this is sign up page</h1>
    )
}

Home Page

import React from 'react'
export default function Home() {
  return (
    <div>
        <h1>Welcome to React System</h1>
    </div>
  )
}

OK. So far we have created a login page, sign-up page, and home page. Now we will configure the “App.js” file. Before configuring the file, remove everything from “App.js”. To configure the “App.js” file let’s follow the steps mentioned below:

  • Import essential files
  • Create a functional component
  • Configure the routers

Code:

import './App.css';
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Login from './pages/Login';
import SignUp from './pages/SignUp';
import Home from './pages/Home';

function App() {
  return (
    <>
      <BrowserRouter>
        <Routes>
          <Route path='/' element={<Login/>}/>
          <Route path='/signup' element={<SignUp/>}/>
          <Route path='/home' element={<Home />} />
        </Routes>
      </BrowserRouter>
    </>
  );
}
export default App;

Now let’s see whether  our router has been configured properly or not simply visit http://localhost:3000/ and we should get the login page as output:

visit http://localhost:3000/signup and we should get sign up page as output:

visit http://localhost:3000/home and we should get sign up page as output:

Great… Now we are ready to move to the next step.

How to Use Tailwind CSS

Using tailwind CSS is as simple as water. We just need to add a class name with HTML tags, nothing else. To get all tailwind CSS class names click here.

Login Page

Now we will implement the actual login page using React.js and tailwind CSS.

Firstly, we will create a simple form using HTML

Code:

import React from 'react'
export default function Login() {
    return (
        <div>
            <div>
                <h1>Sign in</h1>
                <form >
                    <div>
                        <label>Email</label>
                        <input/>
                    </div>
                    <div >
                        <label>Password</label>
                        <input type="password"/>
                    </div>
                    <a href="#" >
                        Forget Password?
                    </a>
                    <div>
                        <button>
                            Login
                        </button>
                    </div>
                </form>
                <p>Don't have an account?<b>Sign up</b></p>
            </div>
        </div>
    )
}

Output:

Now it’s the perfect time to make up our login page. Let’s make it more beautiful using Tailwind CSS.

Now we will make beautiful the header tag

Code:

<h1 className="text-3xl font-semibold text-center text-purple-700 underline uppercase decoration-wavy">
  Sign in
</h1>

Output:

Now, we will add style with an input box. On the login page, we have a total of two input boxes email and password. To make the input fields beautiful we need to add some classes. Let’s add it.

<div className="mb-2">
        <label
            for="email"
            className="block text-sm font-semibold text-gray-800"
        >
            Email
        </label>
        <input
            type="email"
            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
        />
    </div>
    
    <div className="mb-2">
        <label
            for="password"
            className="block text-sm font-semibold text-gray-800"
        >
            Password
        </label>
        <input
            type="password"
            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
        />
    </div>

Output:

Now, we will work with “forgot password option”

<ahref="#" className="text-xs text-purple-600 hover:underline"
    > Forget Password? </a>

Output:

Now, it is far better than the previous one. Here, we have one button “Login”, it’s time to polish it.

Code:

<div className="mt-6">
        <button className="w-full px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-purple-700 rounded-md hover:bg-purple-600 focus:outline-none focus:bg-purple-600">
            Login
        </button>
    </div>

Output:

Now, we will work on the last part.

Code:

<p className="mt-8 text-xs font-light text-center text-gray-700">
        {" "}
        Don't have an account?{" "}
        <b
            className="font-medium text-purple-600 hover:underline cursor-pointer"
        >
            Sign up
        </b>
    </p>

Woow… we did a great job together, superb. Now we will polish the div whose id is “child-container”.

Code:

className="w-full p-6 m-auto bg-white rounded-md ring ring-2 ring-purple-600 lg:max-w-xl"

r

It looks great right,  I think, it would be better if we can take the form horizontally center. OK, then let’s do it. Add the classes mentioned below with the most outer div.

Code:

className=" bg-gray-100 relative flex flex-col justify-center min-h-screen overflow-hidden"

Output

Awesome, we did a great job. So far we have created the login page. Now we need to handle the form. To handle the form, we will create a state

const [formInput, setFormInput] = React.useState({
        email: "",
        password: "",
    })

Now, we will connect the input fields of the login page with the useState. To do this, we need to add “formInput.email” in the email input field and “formInput.password” in the password input field as values.

Now we will create a function to read data from the form if a single value begins to change. To do this, we need to create an input handle function. Let’s create it first.

setFormInput({ ...formInput, email: e.target.value })
setFormInput({ ...formInput, password: e.target.value })

Need to add the code with the onChange event of email and password input.

Great, our form handling is almost ready. Now we just need to create a form handler.  So, without wasting time let’s do it.

const handleForm = () => {
        console.log(formInput)
        setFormInput({
            email: "",
            password: "",
        })
    }

We have to call this function with “onClick” event of the login button. We will call it there.

onClick={()=>{handleForm()}}

Ok, so far we have done the login form handler successfully.

Note: In the “handleForm” function, we need to add some back-end functionality. We will do it while we will working with the back-end server.

Now we need to implement the navigation system. To add a navigation system we need to import  “useNavigate”

import { useNavigate } from 'react-router-dom'

Then, we need to create an object inside the react functional component.

const navigateTo = useNavigate()

Now we will write a callback function inside the “Sign Up” context when the user will click on the “Sign up” this function will be called.

onClick={() => navigateTo("/signup")}

“/signup” is our path, that’s why we need to pass this as the parameter.

Login Page Code:

import React from 'react'
import { useNavigate } from 'react-router-dom'
export default function Login() {
    const navigateTo = useNavigate()
    const [formInput, setFormInput] = React.useState({
        email: "",
        password: "",
    })
    const handleForm = () => {
        console.log(jwt)
        setFormInput({
            email: "",
            password: "",
        })
    }

    return (
        <div className=" bg-gray-100 relative flex flex-col justify-center min-h-screen overflow-hidden">
            <div id="child-container" className="w-full p-6 m-auto bg-white rounded-md ring ring-2 ring-purple-600 lg:max-w-xl">
                <h1 className="text-3xl font-semibold text-center text-purple-700 underline uppercase decoration-wavy">
                    Sign in
                </h1>
                <form>
                    <div className="mb-2">
                        <label
                            for="email"
                            className="block text-sm font-semibold text-gray-800"
                        >
                            Email:
                        </label>
                        <input
                            onChange={(e) => setFormInput({ ...formInput, email: e.target.value })}
                            value={formInput.email}
                            type="email"
                            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
                        />
                    </div>
                    <div className="mb-2">
                        <label
                            for="password"
                            className="block text-sm font-semibold text-gray-800"
                        >
                            Password
                        </label>
                        <input
                            onChange={(e) => setFormInput({ ...formInput, password: e.target.value })}
                            value={formInput.password}
                            type="password"
                            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
                        />
                    </div>
                    <a
                        href="#"
                        className="text-xs text-purple-600 hover:underline"
                    >
                        Forget Password?
                    </a>
                    <div className="mt-6">
                        <button onClick={() => { handleForm() }} className="w-full px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-purple-700 rounded-md hover:bg-purple-600 focus:outline-none focus:bg-purple-600">
                            Login
                        </button>
                    </div>
                </form>
                <p className="mt-8 text-xs font-light text-center text-gray-700">
                    {" "}
                    Don't have an account?{" "}
                    <b
                        onClick={() => navigateTo("/signup")}
                        className="font-medium text-purple-600 hover:underline cursor-pointer"
                    >
                        Sign up
                    </b>
                </p>
            </div>
        </div>
    )
}

Output:

Sign Up Page

So far, we have created a Login page, now we will implement a sign-up page. Implementing a sign-up page is easy because we have already implemented the sign-in page. What we will do is, we will copy the code of the login page and paste it to the signup page.  Now we can see the output of the signup page, which is the same as the login page. Now, we will make some changes here:

  1. Write ‘Sign Up’ instead of ‘Sign In’
  2. On the login page, we have a total of two inputs (email and password). Here we need a total of four input boxes. We can do it by duplicating the existing one and changing their label to “User Name”, “Email”, “Phone Number” and “Password”.
  3. Remove the “Forgot Password” option
  4. Change the context of the button from “Login” to “Registration”
  5. Change the context of the last p tags from “” to “Already have an account? Sign in

I won’t discuss deeply implementing the sign-up page. Because it is the same as the login page. So take a rest, have a tea and implement it by yourself.

Great… Now our sign-up page is ready.

Sign-up page code:

import React from 'react'
import { useNavigate } from 'react-router-dom'

export default function SignUp() {
    const navigateTo = useNavigate()
    const [formInput, setFormInput] = React.useState({
        userName: "",
        email: "",
        phoneNumber: "",
        password: ""
    })

    const handleForm = () => {
console.log(formInput)
       setFormInput({
        userName: "",
        email: "",
        phoneNumber: "",
        password: ""
    })

    }

    return (
        <div className="bg-gray-100 relative flex flex-col justify-center min-h-screen overflow-hidden">
            <div className="w-full p-6 m-auto bg-white rounded-md ring ring-2 ring-purple-600 lg:max-w-xl">
                <h1 className="text-3xl font-semibold text-center text-purple-700 underline uppercase decoration-wavy">
                    Sign up
                </h1>
                <form>
                    <div className="mb-2">
                        <label
                            for="name"
                            className="block text-sm font-semibold text-gray-800"
                        >
                            User Name
                        </label>
                        <input
                            onChange={(e) => setFormInput({ ...formInput, userName: e.target.value })}
                            value={formInput.userName}
                            type="name"
                            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
                        />
                    </div>
                    <div className="mb-2">
                        <label
                            for="email"
                            className="block text-sm font-semibold text-gray-800"
                        >
                            Email
                        </label>
                        <input
                            onChange={(e) => setFormInput({ ...formInput, email: e.target.value })}
                            value={formInput.email}
                            type="email"
                            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
                        />
                    </div>
                    <div className="mb-2">
                        <label
                            for="number"
                            className="block text-sm font-semibold text-gray-800"
                        >
                            Phone Number
                        </label>
                        <input
                            onChange={(e) => setFormInput({ ...formInput, phoneNumber: e.target.value })}
                            value={formInput.phoneNumber}
                            type="number"
                            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
                        />
                    </div>
                    <div className="mb-2">
                        <label
                            for="password"
                            className="block text-sm font-semibold text-gray-800"
                        >
                            Password
                        </label>
                        <input
                            onChange={(e) => setFormInput({ ...formInput, password: e.target.value })}
                            value={formInput.password}
                            type="password"
                            className="block w-full px-4 py-2 mt-2 text-purple-700 bg-white border rounded-md focus:border-purple-400 focus:ring-purple-300 focus:outline-none focus:ring focus:ring-opacity-40"
                        />
                    </div>
                    <div className="mt-6">
                        <button onClick={() => { handleForm() }} className="w-full px-4 py-2 tracking-wide text-white transition-colors duration-200 transform bg-purple-700 rounded-md hover:bg-purple-600 focus:outline-none focus:bg-purple-600">
                            Registration
                        </button>
                    </div>
                </form>
                <p className="mt-8 text-xs font-light text-center text-gray-700">
                    {" "}
                    Already have an account?{" "}
                    <b
                        onClick={() => navigateTo("/")}
                        className="font-medium text-purple-600 hover:underline cursor-pointer"
                    >
                        Sign in
                    </b>
                </p>
            </div>
        </div>
    )
}

Output:

Creating the Backend

So far we have created a login UI, and now we will create a backend server. We will create a backend server using express.js where the array will be used as a database and JWT will be used to authenticate users. To create a server, let’s create a directory called ‘server’ and open the directory using vs code.

Then open up its terminal of it and run the following commands:

npm init -y
npm install express
npm i jsonwebtoken

Create a file called “index.js” inside server directory.

write the following code inside the “index.js” file. Here, we are creating an express service that will run on port 5000.

const express = require('express');
const app = express();
const PORT = 5000;
app.use(express.json())
app.listen(PORT, (error) =>{
    if(!error)
        console.log("Server is Successfully Running, and App is listening on port "+ PORT)
    else 
        console.log("Error occurred, server can't start", error);
    }
);

Great… Now our express server is ready to run.

To run the server, run:

node index.js

We will try to keep the server very simple, that’s why we will not use any complex coding style.  It’s time to make our first API for the sign-up system.

We will store users’ data inside an array called “users”, so first create the array

const users = [];

Now, we will create a data format, and according to that format, data will be stored inside the array.

const userModel = (userName, email, phoneNumber, password) => {
    return { userName, email, phoneNumber, password }
}

Our simple database is ready to use. Now we will create our sign-up API.  The signup API will receive data from users as object where the key will be like this “userName, email, phoneNumber, password”.

One email cannot register more than one time and after successful signup, it will return an object containing “message” and “token” key. The token will be used to authenticate a user.

Code


app.post("/signup", (req, res) => {
    let fetchedData = users[
        users.findIndex(
            retrivedData => retrivedData['email'] === req.body.email
        )
    ]
    if (fetchedData) {
        res.send({ message: "Email already exist!" })
    }
    else {
        users.push(userModel(
            req.body.userName,
            req.body.email,
            req.body.phoneNumber,
            req.body.password
        ));
        const token = jwt.sign(
            {
                userName: req.body.userName,
                email: req.body.email,
            },
            "secreact_key",
            { expiresIn: "7d" }
        );
        res.send({
            message: "User has been created sucessfully",
            token: token
        })
    }
})

Code:

app.post("/login", (req, res) => {
    let fetchedData = users[
        users.findIndex(
            retrivedData => retrivedData['email'] === req.body.email && retrivedData['password'] === req.body.password
        )
    ]
    if (fetchedData) {
        const token = jwt.sign(
            {
                userName: fetchedData.userName,
                email: fetchedData.email,
            },
            "secreact_key",
            { expiresIn: "7d" }
        );
        res.status(200).send({
            message: "Login sucessfully",
            token: token
        })
    }
    else {
        res.send({ message: "User not found" })
    }
})

Awesome.. our server is ready now.

Server.js


const express = require('express');
const app = express();
const jwt = require("jsonwebtoken");
const PORT = 5000;
app.use(express.json())
const users = [];
const userModel = (userName, email, phoneNumber, password) => {
    return { userName, email, phoneNumber, password }
}
app.post("/signup", (req, res) => {
    let fetchedData = users[
        users.findIndex(
            retrivedData => retrivedData['email'] === req.body.email
        )
    ]
    if (fetchedData) {
        res.status(400).send({ message: "Email already exist!" })
    }
    else {
        users.push(userModel(
            req.body.userName,
            req.body.email,
            req.body.phoneNumber,
            req.body.password
        ));
        const token = jwt.sign(
            {
                userName: req.body.userName,
                email: req.body.email,
            },
            "secreact_key",
            { expiresIn: "7d" }
        );
        res.status(200).send({
            message: "User has been created sucessfully",
            token: token
        })
    }
})

app.post("/login", (req, res) => {
    let fetchedData = users[
        users.findIndex(
            retrivedData => retrivedData['email'] === req.body.email && retrivedData['password'] === req.body.password
        )
    ]
    if (fetchedData) {
        const token = jwt.sign(
            {
                userName: fetchedData.userName,
                email: fetchedData.email,
            },
            "secreact_key",
            { expiresIn: "7d" }
        );
        res.status(200).send({
            message: "Login sucessfully",
            token: token
        })
    }
    else {
        res.status(400).send({ message: "User not found" })
    }
})



app.listen(PORT, (error) => {
    if (!error)
        console.log("Server is Successfully Running, and App is listening on port " + PORT)
    else
        console.log("Error occurred, server can't start", error);
}
);

Connecting Back-end With Front-end

So far we have created one back-end application and one front-end application. Now we need to connect both of them using “Axios”. To install “Axios” run the command on the front-end terminal.

npm i axios

Now, let’s create a folder called “services” and inside the folder make a file called “api.js”. In this file, we will call the API from the back-end server.

api.js

import axios from "axios";
const BASE_URL = "http://localhost:5000";
export const signup = (data) => {
    return axios.post(`${BASE_URL}/signup`, data, {
        headers: { "Content-Type": "application/json" },
    })
}
export const login = (data) => {
    return axios.post(`${BASE_URL}/login`, data, {
        headers: { "Content-Type": "application/json" },
    })
}

Here, we have just imported “Axios” at the very first line, then we created a variable called “BASE_URL”, which is nothing but the “URL” of the back-end server. The first function calls the “signup” API and in the second function “login”, we have called the “login” API. Then We set the content type to JSON because we will send json type data through the API call. The ‘data’ parameter will carry the JSON data. That’s it. Now we will call these functions in the login button and sign-up button.

Signup Function

Now we are going to add signup API with the front-end. To do this, firstly we need to import the API.

import { signup } from '../services/api'

Edit the “handleForm” function like this:

const handleForm=()=>{
        signup(formInput)
        .then(res=>{
            setFormInput({
                userName: "",
                email: "",
                phoneNumber: "",
                password: ""
            })
        })
        .catch(error=>{
        })
        
    }

To handle login and signup functionality, we need to use a state management system. For this, we will use “Context API”. Now we will implement context API to authenticate users using “JWT”.

Code:

import { createContext } from 'react';
export const AuthContext = createContext();

Add the code to the App.js file.

Now we need to configure the App.js file. To do this, first, need to create a state.

const [jwt, setJWt] = useState(null)

Then, wrap the router using “AuthContext” as shown below:

<BrowserRouter>
        <AuthContext.Provider value={{
              auth: [jwt, setJWt],
            }}>
          <Routes>
           ..........
          </Routes>
        </AuthContext.Provider>
      </BrowserRouter>

Great, our context API is ready now. Now, we are good to do sign-up.

In “signup.js” file, we need to import the context API.

code:

import { useContext } from 'react'
import { AuthContext } from '../App'

code:

const { auth } = useContext(AuthContext)
const [jwt, setJWt] = auth

We have successfully imported the Context API. Now we have to implement the functionality. To do it, set just set the jwt state if the API sends an authentic result. Let’s make a function to check user authentication. Create a file called routeHandler.js inside “services” directory.

export const routeHandler=(navigateTo, jwt)=>{
    if (jwt != null && Object.keys(jwt).length){
        navigateTo("/home")
    }
}

import the function “routeHandler” and implement the functionality:

import { routeHandler } from '../services/routerHandler'
routeHandler(navigateTo, jwt)

Now we need to complete the handleForm function

const handleForm = () => {
        signup(formInput)
            .then(res => {
                setFormInput({
                    userName: "",
                    email: "",
                    phoneNumber: "",
                    password: ""
                })
                setJWt(res.data)
                navigateTo("/home")
            })
            .catch(error => {
                alert(error.message)
            })
    }

If the user successfully signs up, then the user will be redirected to the home page and won’t be able to access the sign-up page until logs out.

Cors

While you will try the functionality that we have implemented so far, you will face an issue called “cors origin blocked”.  Actually, it is possible to write a huge blog like this on “cors”, So in this blog, I won’t talk about it too much. To solve this we need to add this code to the server.js file

const cors = require("cors")
app.use(cors())

Login Function

I won’t discuss implementing everything on the login page. You have to follow the same process that we did on the sign-up page. Have a cup of tea, go out of the site to inhale the fresh air, and then start to implement the login page functionality with fresh. It is your assignment.

Conclusion

Creating a project using React.Js and Tailwind CSS is a great way to quickly build a page, which will be highly responsive and gorgeous.

React Js allows developers to implement highly interactive components while tailwind CSS provides a wind range of style options and customization features to ensure that the page looks professional.

The combination of these two technologies can be a great package for any website. You can download CodeSource from GitHub.