CodingMSTR LogoCodingMSTR
Building a Login and Registration Form with Validation in React and Tailwind CSS

Building a Login and Registration Form with Validation in React and Tailwind CSS

Free

Here is the example of a Login and Registration Form with Validation Created in React JS Using Tailwind CSS 

Category: React
Added On: October 27, 2024
Developer: By Praveen
Demo/LiveYoutube Video

For any customization or code setup, feel free to contact us. We also offer deployment on live servers.

For any issues related to downloading, email me at devpraveenkr@gmail.com

Need additional support or customization? Contact me!

Project Screenshots

No screenshots available

Project Description

In this article, we’ll create a Login and Registration form in React, using Tailwind CSS for styling and react-hook-form with Yup for validation.

The example form will validate user inputs for email and password, providing clear error messages when fields are filled out incorrectly. We’ll also use react-router-dom for navigation and react-hot-toast for user notifications.

Prerequisites

  • Basic knowledge of React
  • Familiarity with Tailwind CSS and React Router
  • Basic understanding of form validation with Yup

Setup

  1. Install Required Packages: Before we start, install the required dependencies:
    bash

    npm install react-router-dom react-hook-form @hookform/resolvers yup react-hot-toast axios tailwindcss


  2. Initialize Tailwind CSS: Tailwind CSS requires initial setup. To get started, follow the steps on the Tailwind CSS installation guide.

Form Schema

We’ll use Yup for validation. This example sets rules for email and password fields.
The email must be a valid format, while the password must be 8-20 characters long and differ from the email.

const schema = yup.object().shape({
    name: yup.string().required('Please enter name'),
    email: yup.string().email().required('Please enter email address'),
    password: yup.string()
        .min(8, 'Password must be at least 8 characters')
        .max(20, 'Password must not exceed 20 characters')
        .notOneOf([yup.ref('email'), null], 'Password must not be the same as your email address')
        .required('Please enter password'),
    confirmPassword: yup.string()
        .oneOf([yup.ref('password'), null], 'Passwords must match')
        .required('Please confirm your password'),
})



Creating the Form Component

The Login component will use react-hook-form for easy validation handling, and Yup as a validation resolver. When the user submits valid data, we’ll attempt to register the user via an API call (using Axios). Feedback is displayed using react-hot-toast.
Here’s the complete code:


Login Component


import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import toast, { Toaster } from 'react-hot-toast';
import axios from 'axios';

const schema = yup.object().shape({
    email: yup.string().email().required('Please enter email address'),
    password: yup.string()
      .min(8, 'Password must be at least 8 characters')
      .max(20, 'Password must not exceed 20 characters')
      .notOneOf([yup.ref('email'), null], 'Password must not be the same as your email address')
      .required('Please enter password'),
  })


export default function Login() {
    const navigate = useNavigate();

    const { register, handleSubmit, formState: { errors } } = useForm({
        resolver: yupResolver(schema),
        defaultValues: {
            email: '',
            password: '',
            confirmPassword: '',
        }
    });

    const onSubmitNew = async (data) => {
        console.log(data);
        try {
            const res = await axios.post('/api/auth/register', data);
            toast.success('User registered successfully.');
            navigate('/');
        } catch (error) {
            toast.error(error.response?.data?.message || 'Something went wrong');
        }
    };

    return (
        <div className="flex items-center justify-center min-h-screen bg-blue-50">
            <Toaster />
            <form onSubmit={handleSubmit(onSubmitNew)}
                className="w-full max-w-md p-8 space-y-4 bg-white shadow-md rounded-2xl"
            >
                <div>
                    <h2 className="text-2xl font-bold mb-7 text-center mt-3 text-blue-900">Login</h2>

                    <div className="mt-5">
                        <label htmlFor="email" className="text-sm font-semibold mb-1">
                            Email
                        </label>
                        <input
                            className="w-full px-3 py-2 border rounded"
                            type="email"
                            placeholder="Email"
                            {...register("email")}
                        />
                        {errors?.email && (
                            <p className="text-red-500 text-xs mt-1">{errors.email?.message}</p>
                        )}
                    </div>


                    <div className="mt-5">
                        <label htmlFor="password" className="text-sm font-semibold mb-1">
                            Password
                        </label>
                        <input
                            className="w-full px-3 py-2 border rounded"
                            type="password"
                            placeholder="Password"
                            {...register("password")}
                        />
                        {errors?.password && (
                            <p className="text-red-500 text-xs mt-1">{errors.password?.message}</p>
                        )}
                    </div>

                    <div className="mt-8">
                        <button
                            className="w-full px-3 py-2 text-white bg-blue-950
                            hover:bg-blue-900 rounded-md"
                            type="submit"
                        >
                            <span className="text-sm">
                                LOGIN
                            </span>
                        </button>
                    </div>

                    <div className="mt-5 text-center">
                        <p className="text-[13px] cursor-pointer text-slate-400 mt-1">
                            Already have an account?
                            <span className="ml-1 font-semibold hover:text-blue-900 text-slate-700"
                                onClick={() => {
                                    navigate('/auth/register')
                                }}
                            >
                                Register
                            </span>
                        </p>
                    </div>

                </div>
            </form>
        </div>
    );
}


Signup Component
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import toast, { Toaster } from 'react-hot-toast';
import axios from 'axios';

const schema = yup.object().shape({
    name: yup.string().required('Please enter name'),
    email: yup.string().email().required('Please enter email address'),
    password: yup.string()
        .min(8, 'Password must be at least 8 characters')
        .max(20, 'Password must not exceed 20 characters')
        .notOneOf([yup.ref('email'), null], 'Password must not be the same as your email address')
        .required('Please enter password'),
    confirmPassword: yup.string()
        .oneOf([yup.ref('password'), null], 'Passwords must match')
        .required('Please confirm your password'),
})


export default function Login() {
    const navigate = useNavigate();

    const { register, handleSubmit, formState: { errors } } = useForm({
        resolver: yupResolver(schema),
        defaultValues: {
            email: '',
            password: '',
            confirmPassword: '',
        }
    });

    const onSubmitNew = async (data) => {
        console.log(data);
        try {
            const res = await axios.post('/api/auth/register', data);
            toast.success('User registered successfully.');
            navigate('/auth/login');
        } catch (error) {
            toast.error(error.response?.data?.message || 'Something went wrong');
        }
    };

    return (
        <div className="flex items-center justify-center min-h-screen bg-blue-50">
            <Toaster />
            <form onSubmit={handleSubmit(onSubmitNew)}
                className="w-full max-w-md p-8 space-y-4 bg-white shadow-md rounded-2xl"
            >
                <div>
                    <h2 className="text-2xl font-bold mb-7 text-center mt-3 text-blue-900">
                        Register
                    </h2>
                    <div>
                        <label htmlFor="email" className="text-sm font-semibold mb-1">
                            Name
                        </label>
                        <input
                            className="w-full px-3 py-2 border rounded"
                            type="text"
                            placeholder="Name"
                            {...register("name")}
                        />
                        {errors?.name && (
                            <p className="text-red-500 text-xs mt-1">{errors.name?.message}</p>
                        )}
                    </div>

                    <div className="mt-5">
                        <label htmlFor="email" className="text-sm font-semibold mb-1">
                            Email
                        </label>
                        <input
                            className="w-full px-3 py-2 border rounded"
                            type="email"
                            placeholder="Email"
                            {...register("email")}
                        />
                        {errors?.email && (
                            <p className="text-red-500 text-xs mt-1">{errors.email?.message}</p>
                        )}
                    </div>


                    <div className="mt-5">
                        <label htmlFor="password" className="text-sm font-semibold mb-1">
                            Password
                        </label>
                        <input
                            className="w-full px-3 py-2 border rounded"
                            type="password"
                            placeholder="Password"
                            {...register("password")}
                        />
                        {errors?.password && (
                            <p className="text-red-500 text-xs mt-1">{errors.password?.message}</p>
                        )}
                    </div>

                    <div className="mt-5">
                        <label htmlFor="confirmPassword" className="text-sm font-semibold mb-1">
                            Confirm Password
                        </label>
                        <input
                            className="w-full px-3 py-2 border rounded"
                            type="password"
                            placeholder="Confirm Password"
                            {...register("confirmPassword")}
                        />
                        {errors?.confirmPassword && (
                            <p className="text-red-500 text-xs mt-1">{errors.confirmPassword?.message}</p>
                        )}
                    </div>


                    <div className="mt-8">
                        <button
                            className="w-full px-3 py-2 text-white
                            bg-blue-950 hover:bg-blue-900 rounded-md"
                            type="submit"
                        >
                            <span className="text-sm">
                                SIGN UP
                            </span>
                        </button>
                    </div>

                    <div className="mt-5 text-center">
                        <p className="text-[13px] cursor-pointer text-slate-400 mt-1">
                            Already have an account?
                            <span className="ml-1 font-semibold hover:text-blue-900 text-slate-700"
                                onClick={() => {
                                    navigate('/auth/login')
                                }}
                            >
                                Sign In
                            </span>
                        </p>
                    </div>

                </div>
            </form>
        </div>
    );
}




Explanation of the Code

  • Form Setup: We use useForm from react-hook-form, passing the Yup schema as a resolver to yupResolver.
  • API Request: When the form is submitted, onSubmitNew is triggered, making a POST request to /api/auth/register. Upon success, a toast notification is displayed, and the user is navigated back to the home page.
  • Styling with Tailwind CSS: The form and its fields are styled using Tailwind CSS classes. The form is centrally aligned on the screen and designed to be responsive.
  • Validation Error Messages: Error messages from Yup validation display under each input field if validation fails.

Project Demo Video