Create a Secure React JS Login Form with Validation (Source Code)

Faraz

By Faraz -

Learn how to create a login form in React JS with robust validation techniques. Build secure authentication systems for your web applications effortlessly.


Create a Secure React JS Login Form with Validation.webp

Table of Contents

  1. Project Introduction
  2. HTML Code
  3. CSS Code
  4. JavaScript Code
  5. Preview
  6. Conclusion

Welcome to our comprehensive guide on building a secure login form in React JS with validation. In today's digital landscape, user authentication is paramount for safeguarding sensitive information and ensuring a seamless user experience. React JS, with its robust ecosystem and state-of-the-art features, provides an ideal platform for creating dynamic and interactive web applications.

Throughout this tutorial, we'll walk you through each step of the process, from setting up your React project environment to implementing form validation and handling form submission. Whether you're a beginner or an experienced developer, this guide will equip you with the knowledge and tools necessary to create a reliable and user-friendly login system.

So, let's dive in and explore the world of React JS login form development together!

Setting Up React Project Environment

To get started, make sure you have Node.js and npm installed on your system. Create a new React project using the Create React App or any preferred method.

How to Set Up React Project Environment:

  1. Install Node.js and npm.
  2. Create a new React project using Create React App: npx create-react-app login-form.
  3. Navigate to the project directory: cd login-form.

Source Code

Step 1 (HTML Code):

When you build a React application, you typically have a JavaScript file where you define your main application logic. This file is called index.js

Inside this main JavaScript file (index.js), you import various React components that make up your application's UI and functionality.

ReactDOM Rendering: React provides a library called ReactDOM which is responsible for rendering React components into the HTML DOM. In the index.js file, you'll typically find a line of code that uses ReactDOM to render your top-level component into the HTML DOM. This is where the <div id="root"></div> comes into play.

The <div id="root"></div> acts as a mounting point or a target DOM element where your React application will be injected. When ReactDOM.render() is called, it takes your top-level React component (usually a component representing the entire application) and injects it into this <div id="root"></div>, effectively rendering your entire application within this single HTML element.

Step 2 (CSS Code):

Define your styles in a separate CSS file and import them into your component. For example import './LoginForm.css';

Let's break down each part:

1. *, *::before, *::after: This targets all elements, pseudo-elements before, and pseudo-elements after, setting their box-sizing to border-box. This ensures that padding and border are included in the element's total width and height.

2. body: Styles for the body of the document. It sets the font family to Montserrat or sans-serif if Montserrat is not available. It makes the body occupy the full viewport height (100vh). It uses CSS Grid to center its content both horizontally and vertically. It sets the text color to a shade of gray (#4f546c) and the background color to a shade of blue (#5930ff).

3. .container: This class styles a container element within the body. It gives it a white background, some padding, and a slight border-radius for rounded corners. The width is set to 20rem.

4. .container__header: Styles for the header within the container. It adds a margin at the bottom to separate it from other elements.

5. .form: Styles for a form element. It uses a flexbox to stack its child elements vertically, with a gap of 2rem between them.

6. .form__group: Styles for a group of form elements. It positions the elements relatively and stacks them vertically.

7. .form__label: Styles for labels within form elements. They are positioned absolutely to sit on top of their respective inputs. They have a white background, some padding, and a smaller font size.

8. .form__input: Styles for input fields. They have padding, inherit font family and color, no outline, a border, and a border-radius.

9. .form__successicon, .form__erroricon: Styles for success and error icons within form elements. They are positioned absolutely at the top right corner, with a small width. By default, they are set to display:none.

10. .form__success, .form__error: Styles applied when the form input is in a success or error state, respectively. They change the border color of the input and display the corresponding icon.

11. .form__message: Styles for error messages are displayed below the form inputs. They have a smaller font size, a slight margin at the top, and a red color. By default, they are set to display:none.

12. .form__button: Styles for form buttons. They have no outline or border, some padding, a border radius, inherit font family, a cursor pointer, a blue background color, and a white text color.

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: Montserrat, sans-serif;
  height: 100vh;
  display: grid;
  justify-content: center;
  align-items: center;
  color: #4f546c;
  background-color: #5930ff;
}

.container {
  background-color: white;
  padding: 2rem;
  border-radius: 0.2rem;
  width: 20rem;
}
.container__header {
  margin-bottom: 2rem;
}

.form {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}
.form__group {
  display: flex;
  flex-direction: column;
  position: relative;
}
.form__label {
  position: absolute;
  top: -0.5rem;
  left: 0.5rem;
  background-color: white;
  padding: 0 0.5rem;
  font-size: 0.8rem;
}
.form__input {
  padding: 1rem;
  font-family: inherit;
  color: inherit;
  outline: none;
  border: 1px solid #c7ccdb;
  border-radius: 0.2rem;
}
.form__successicon, .form__erroricon {
  position: absolute;
  right: 1rem;
  top: 1rem;
  width: 1rem;
  display: none;
}
.form__successicon {
  fill: #00c853;
}
.form__erroricon {
  fill: #ff1744;
}
.form__message {
  font-size: 0.8rem;
  margin-top: 0.2rem;
  color: #ff1744;
  display: none;
}
.form__success input {
  border: 1px solid #00c853;
}
.form__success svg.form__successicon {
  display: block;
}
.form__error input {
  border: 1px solid #ff1744;
}
.form__error svg.form__erroricon {
  display: block;
}
.form__error p.form__message {
  display: block;
}
.form__button {
  outline: none;
  border: none;
  padding: 1rem;
  border-radius: 0.2rem;
  font-family: inherit;
  cursor: pointer;
  background-color: #5930ff;
  color: white;
} 

Step 3 (JavaScript Code):

Let's create a new component for our login form. We'll use JSX to define the structure of the form and React hooks for managing its state.

How to Create the Login Form Component:
  1. Create a new file named LoginForm.js.
  2. Define the structure of the form using JSX.
  3. Implement the useState hook to manage form fields' state.

Let's break down the code step by step:

1. Imports:

  • useState, useEffect, useRef, StrictMode, and ChangeEvent are destructured from the React library.
  • createRoot is destructured from the ReactDOM library.

2. Root Element and Root Creation:

  • It selects the DOM element with the id 'root'.
  • It creates a root using createRoot from ReactDOM.

3. App Component:

  • It defines the main functional component App.
  • Initializes state using useState with an initial state object containing email and password fields.
  • Utilizes useRef to create references for email input, password input, and the submit button.
  • Defines functions for handling change, blur, keyup events, and form submission.
  • Checks and validates email and password inputs.
  • Renders a form with email input, password input, and a submit button.
  • It utilizes conditional rendering to display success or error icons and messages based on input validation.

4. Input Validation Functions:

  • isEmail: Validates email using a regular expression.
  • isPass and isPass2: Validates password based on certain criteria such as lowercase, uppercase, numeric, special characters, and length.
  • setErrorFor and setSuccessFor: Functions to set error and success styles/messages for inputs.

5. Event Handlers:

  • handleChange: Updates the form state on input change.
  • handleBlur: Handles blur events to validate inputs.
  • handleKeyUp: Handles keyup events to validate inputs.
  • handleSumit: Handles form submission, checks inputs, and displays loading spinner on successful submission.

6. Rendering:

  • Renders the App component within a StrictMode component using root.render.
const {useState, useEffect, useRef, StrictMode,  ChangeEvent  } = React
const {createRoot, } = ReactDOM

const rootElement = document.getElementById('root')
const root = createRoot(rootElement)

const App =()=>{
    const initialState ={
    email:'',
    password:''
  }
   
  const [formValue, setFormValue] = useState(initialState)
 
   const email = useRef(null)
    const smallsEmail = useRef(null)
    const password = useRef(null)
    const smallsPass = useRef(null)
    const button = useRef(null)
    const handleChange = ({target:{name,value}})=>{
    setFormValue({...formValue, [name]:value})
  }
 const handleBlur = ({target:{name,value}}, input ) =>{
if (input.current.name === 'email') {
    if (!isEmail(input.current.value.trim() )) {
		setErrorFor(input, 'Did not enter a valid email', smallsEmail.current);
	}else {
		setSuccessFor(input);
	}
}
   if (input.current.name === 'password'){
     isPass(input)
   }
    }
  
 const handleSumit = (e)=>{
   	e.preventDefault();
    
    checkInputs()	
   if(isEmail(email.current.value.trim()) && isPass2(password) ){
     const spiner = document.createElement("i")
    spiner.classList.add("fa", "fa-spinner", "fa-spin")
     console.log(spiner)
    button.current.appendChild(spiner) 
     
   setFormValue({
    email:'',
    password:'',
  })
   
    email.current.value ='',
    password.current.value =''
    } 
   }
 
 const handleKeyUp = ({target:{name, value}}, input) =>{

  if (input.current.name === 'email') {
    if (!isEmail(input.current.value.trim() )) {
		setErrorFor(input, 'Did not enter a valid email', smallsEmail.current);
	}else {
		setSuccessFor(input);
	}
}
   if (input.current.name === 'password'){
     isPass(input)
   }
   
   }
 
 function checkInputs() {
	// trim to remove the whitespaces
	
	const emailValue = email.current.value.trim();
	const passwordValue = password.current.value.trim();
	
	if(emailValue === '') {
		setErrorFor(email, 'You cannot leave the email blank', smallsEmail.current);
	} else if (!isEmail(emailValue)) {
		setErrorFor(email, 'Did not enter a valid email', smallsEmail.current);
    return
	} else {
		setSuccessFor(email);
	}
	
	if(passwordValue === '') {
		setErrorFor(password, 'Password must not be entered blank.', smallsPass.current);
    return
	} else {
    isPass(password)
	}

}
  
 function setErrorFor(input, message, small) {
	const formControl = input.current.parentElement;
    formControl.classList.remove("form__success")
	formControl.classList.add("form__error");
	 small.innerHTML = message
}
  
  function isPass(password){
  const isLowerCase = /(?=[a-z])/.test(password.current.value);
  const isUpperCase = /(?=[A-Z])/.test(password.current.value);
    const isNumber = /(?=\d)/.test(password.current.value);
    const isSpecialChar = /(?=\W)/.test(password.current.value);
     const isLongEnough = /.{8,}/.test(password.current.value);
   
     if (!isNumber) {
    	setErrorForPass(password, 'The field must contain numbers', smallsPass.current);
        return
	   } else {
		setSuccessForPass(password);
	}
   if (!isLowerCase) {
    	setErrorForPass(password, 'The field must contain lowercase', smallsPass.current);
        return
	   } else {
		setSuccessForPass(password);
	}
     if (!isUpperCase) {
    	setErrorForPass(password, 'The Field must Contain Capital Letters', smallsPass.current);
       return
	   } else {
		setSuccessForPass(password);
	}
     if (!isSpecialChar) {
    	setErrorForPass(password, 'The field must contain Special characters', smallsPass.current);
        return
	   } else {
		setSuccessForPass(password);
	}
     if (!isLongEnough) {
    	setErrorForPass(password, 'The field must contain more than 8 characters', smallsPass.current);
        return
	   } else {
		setSuccessForPass(password);
	}
}

function setSuccessFor(input) {
	const formControl = input.current.parentElement;
	formControl.classList.remove("form__error");
  formControl.classList.add("form__success");
}
  
function setErrorForPass(input, message, small) {
	const formControl = input.current.parentElement;
  formControl.classList.remove("form__success")
	formControl.classList.add("form__error");
	 small.innerHTML = message
}

function setSuccessForPass(input) {
	const formControl = input.current.parentElement;
	formControl.classList.remove("form__error");
  formControl.classList.add("form__success");
}
  
function isEmail(email) {
	return /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(email);
}
 function isPass2(password) {
   const isLowerCase = /(?=[a-z])/.test(password.current.value);
  const isUpperCase = /(?=[A-Z])/.test(password.current.value);
    const isNumber = /(?=\d)/.test(password.current.value);
    const isSpecialChar = /(?=\W)/.test(password.current.value);
     const isLongEnough = /.{8,}/.test(password.current.value);
   if(!isLowerCase){
     return false
   }
   if(!isUpperCase){
     return 
   }
   if(!isNumber){
     return false
   }
   if(!isSpecialChar){
     return 
   }
   if(!isLongEnough){
     return false 
   }
	return true
} 

  return(
    <div className="container">
    <h2 className="container__header">Login</h2>
    <form action="" className="form" onSubmit={handleSumit}>
      <div className="form__group">
        <label htmlFor="email" className="form__label">Email</label>
        <input 
          type="text" 
          className="form__input form__username" 
          name="email"
          id="username" 
          placeholder="Create a username" 
          autoComplete="off"
           onChange={handleChange}
          onKeyUp={(e)=> handleKeyUp(e, email)}
           onBlur={(e)=> handleBlur(e,email)}
          ref={email}
          
          />
        <svg className="form__successicon" viewBox="0 0 512 512" width="100" title="check-circle">
          <path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" />
        </svg>
        <svg className="form__erroricon" viewBox="0 0 512 512" width="100" title="exclamation-circle">
          <path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" />
        </svg>
        <p ref = {smallsEmail} className="form__message"></p>
      </div>

      <div className="form__group">
        <label htmlFor="password" className="form__label">Password</label>
        <input 
          type="password" 
          className="form__input form__password" 
          name="password" 
          id="form__password"            
          placeholder="Create a password" 
          ref={password}
           onChange={handleChange}
           onBlur={(e)=>handleBlur(e,password)}
           onKeyUp= {(e)=> handleKeyUp(e, password)}
          autoComplete="off"
          />
        <svg className="form__successicon" viewBox="0 0 512 512" width="100" title="check-circle">
          <path d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z" />
        </svg>
        <svg className="form__erroricon" viewBox="0 0 512 512" width="100" title="exclamation-circle">
          <path d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z" />
        </svg>
        <p ref ={smallsPass} className="form__message"></p>
      </div>

      <button ref={button} className="form__button">Submit  </button>
    </form>
  </div>
  )
}

root.render(
   <StrictMode>
     <App/>
    </StrictMode>
     )

Final Output:

Create a Secure React JS Login Form with Validation.gif

See the Pen Untitled by Faraz (@codewithfaraz) on CodePen.

Conclusion:

In conclusion, building a login form in React JS with validation is a crucial aspect of front-end development, especially in web applications requiring user authentication. By following the steps outlined in this tutorial, you've learned how to create a robust and secure login system using React's powerful features.

From setting up your project environment to implementing form validation and handling form submissions, you've gained valuable insights into best practices and techniques for building user-friendly interfaces.

With your newfound knowledge and skills, you're well-equipped to tackle complex authentication challenges and contribute to the creation of seamless web experiences.

Thank you for joining us on this journey, and happy coding!

That’s a wrap!

I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.

Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee.

And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!

Thanks!
Faraz 😊

End of the article

Subscribe to my Newsletter

Get the latest posts delivered right to your inbox


Latest Post