Loading

Quipoin Menu

Learn • Practice • Grow

react / Form Validation in React
tutorial

Form Validation in React

Forms are useless if users can submit invalid data. **Form validation** ensures that the data meets your requirements before it's processed. In React, you can implement validation in several ways, from simple inline checks to dedicated validation libraries.

Types of Validation

  • Client-side validation: Immediate feedback, better user experience, but not secure.
  • Server-side validation: Essential for security, final check before processing.

Always validate on the server too! Client-side validation is for user experience, server-side validation is for security.

Basic Validation with Controlled Components
function ValidationForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    age: ''
  });
  const [errors, setErrors] = useState({});

  function validate(name, value) {
    switch (name) {
      case 'username':
        if (value.length < 3) {
          return 'Username must be at least 3 characters';
        }
        if (value.length > 20) {
          return 'Username must be less than 20 characters';
        }
        return '';
      case 'email':
        if (value && !/^[^s@]+@[^s@]+.[^s@]+$/.test(value)) {
          return 'Invalid email format';
        }
        return '';
      case 'age':
        if (value && (parseInt(value) < 18 || parseInt(value) > 120)) {
          return 'Age must be between 18 and 120';
        }
        return '';
      default:
        return '';
    }
  }

  function handleChange(event) {
    const { name, value } = event.target;
    setFormData(prev => ({ ...prev, [name]: value }));
   
    <!-- Validate on change -->
    const error = validate(name, value);
    setErrors(prev => ({ ...prev, [name]: error }));
  }

  function handleSubmit(event) {
    event.preventDefault();
   
    <!-- Validate all fields before submit -->
    const newErrors = {};
    Object.keys(formData).forEach(key => {
      const error = validate(key, formData[key]);
      if (error) newErrors[key] = error;
    });
   
    if (Object.keys(newErrors).length === 0) {
      console.log('Form submitted:', formData);
    } else {
      setErrors(newErrors);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          name="username"
          value={formData.username}
          onChange={handleChange}
          placeholder="Username"
        />
        {errors.username && <span style={{ color: 'red' }}>{errors.username}</span>}
      </div>
      <div>
        <input
          name="email"
          type="email"
          value={formData.email}
          onChange={handleChange}
          placeholder="Email"
        />
        {errors.email && <span style={{ color: 'red' }}>{errors.email}</span>}
      </div>
      <div>
        <input
          name="age"
          type="number"
          value={formData.age}
          onChange={handleChange}
          placeholder="Age"
        />
        {errors.age && <span style={{ color: 'red' }}>{errors.age}</span>}
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

Using HTML5 Built-in Validation

HTML5 provides some built-in validation attributes that work in React too:
<input
  type="email"
  required
  minLength={5}
  maxLength={50}
  pattern="[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,}$"
/>

However, the default browser validation styling and behavior can be inconsistent. Most React developers prefer custom validation.

Real-time Validation with Debouncing

For API calls (like checking if a username is available), you don't want to call the API on every keystroke. Use debouncing:
import { useState, useEffect } from 'react';

function UsernameField() {
  const [username, setUsername] = useState('');
  const [isAvailable, setIsAvailable] = useState(null);
  const [checking, setChecking] = useState(false);

  useEffect(() => {
    const timer = setTimeout(() => {
      if (username.length >= 3) {
        setChecking(true);
        <!-- Simulate API call -->
        fetch(`/api/check-username?username=${username}`)
          .then(res => res.json())
          .then(data => {
            setIsAvailable(data.available);
            setChecking(false);
          });
      }
    }, 500); <!-- Wait 500ms after last keystroke -->

    return () => clearTimeout(timer);
  }, [username]);

  return (
    <div>
      <input
        value={username}
        onChange={(e) => setUsername(e.target.value)}
        placeholder="Choose username"
      />
      {checking && <span>Checking...</span>}
      {isAvailable === false && <span style={{ color: 'red' }}>Username taken</span>}
      {isAvailable === true && <span style={{ color: 'green' }}>Username available</span>}
    </div>
  );
}

Validation Libraries

For complex forms, consider using validation libraries:
  • Formik: Popular form library with built-in validation
  • React Hook Form: Performant form library with easy validation
  • Yup: Schema validation library often used with Formik

Two Minute Drill

  • Form validation ensures user input meets requirements before submission.
  • Always validate on both client (UX) and server (security).
  • Controlled components make validation easier – you have immediate access to values.
  • Store errors in state and display them near the relevant inputs.
  • For API validation (like checking username availability), use debouncing to avoid too many requests.
  • For complex forms, consider using dedicated libraries like Formik or React Hook Form.

Need more clarification?

Drop us an email at career@quipoinfotech.com