Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
LoginSign Up
menu
  1. Home
  2. /Web Development
  3. /React
  4. /Form Validation
Your Progress0%
0 of 42 completed

React Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App

Form Validation

Building bulletproof forms with validation

Forms without validation are like doors without locks - anyone can submit anything! Validation ensures users provide the right data in the right format before submission. You'll learn how to validate forms in React, when to show errors, how to display helpful messages, and patterns for building forms with great user experience. Let's make your forms bulletproof! 🛡️

Validation Basics

Form validation checks if user input meets your requirements:

Basic Required Field Validation

basic-validation.jsx
function Form() {
  const [email, setEmail] = React.useState('')
  const [error, setError] = React.useState('')
  
  const handleSubmit = (e) => {
    e.preventDefault()
    
    // Validate on submit
    if (!email) {
      setError('Email is required')
      return
    }
    
    if (!email.includes('@')) {
      setError('Email must be valid')
      return
    }
    
    // Clear error if valid
    setError('')
    console.log('Submitting:', email)
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <button type="submit">Submit</button>
    </form>
  )
}

// Validates only when user tries to submit

Common Validation Types:

  • Required - Field must not be empty
  • Format - Email, phone, URL patterns
  • Length - Min/max character count
  • Range - Min/max numeric values
  • Custom - Password strength, matching fields

When to Validate

1. On Submit (Basic)

validate-submit.jsx
function Form() {
  const [data, setData] = React.useState({ name: '', email: '' })
  const [errors, setErrors] = React.useState({})
  
  const handleSubmit = (e) => {
    e.preventDefault()
    
    const newErrors = {}
    
    if (!data.name) {
      newErrors.name = 'Name is required'
    }
    
    if (!data.email) {
      newErrors.email = 'Email is required'
    } else if (!data.email.includes('@')) {
      newErrors.email = 'Email must be valid'
    }
    
    setErrors(newErrors)
    
    // Only submit if no errors
    if (Object.keys(newErrors).length === 0) {
      console.log('Form is valid, submitting...')
    }
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        value={data.name}
        onChange={(e) => setData({ ...data, name: e.target.value })}
      />
      {errors.name && <p className="error">{errors.name}</p>}
      
      <input
        name="email"
        value={data.email}
        onChange={(e) => setData({ ...data, email: e.target.value })}
      />
      {errors.email && <p className="error">{errors.email}</p>}
      
      <button type="submit">Submit</button>
    </form>
  )
}

// Pros: Simple, doesn't annoy users while typing
// Cons: User only sees errors after submit attempt

2. On Blur (Better UX)

validate-blur.jsx
function Form() {
  const [email, setEmail] = React.useState('')
  const [error, setError] = React.useState('')
  const [touched, setTouched] = React.useState(false)
  
  const validateEmail = (value) => {
    if (!value) return 'Email is required'
    if (!value.includes('@')) return 'Email must be valid'
    return ''
  }
  
  const handleBlur = () => {
    setTouched(true)
    setError(validateEmail(email))
  }
  
  const handleChange = (e) => {
    setEmail(e.target.value)
    // Only validate if field was already touched
    if (touched) {
      setError(validateEmail(e.target.value))
    }
  }
  
  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={handleChange}
        onBlur={handleBlur}
        placeholder="Email"
      />
      {touched && error && <p className="error">{error}</p>}
    </div>
  )
}

// Pros: Better UX - validates when user leaves field
// Cons: Slightly more complex

3. Real-time (Most Responsive)

validate-realtime.jsx
function Form() {
  const [password, setPassword] = React.useState('')
  const [errors, setErrors] = React.useState([])
  
  const validatePassword = (value) => {
    const newErrors = []
    
    if (value.length < 8) {
      newErrors.push('At least 8 characters')
    }
    if (!/[A-Z]/.test(value)) {
      newErrors.push('At least one uppercase letter')
    }
    if (!/[0-9]/.test(value)) {
      newErrors.push('At least one number')
    }
    
    return newErrors
  }
  
  const handleChange = (e) => {
    const value = e.target.value
    setPassword(value)
    setErrors(validatePassword(value))
  }
  
  return (
    <div>
      <input
        type="password"
        value={password}
        onChange={handleChange}
        placeholder="Password"
      />
      {errors.length > 0 && (
        <ul className="error-list">
          {errors.map((error, i) => (
            <li key={i}>{error}</li>
          ))}
        </ul>
      )}
      {password && errors.length === 0 && (
        <p className="success">Password is strong!</p>
      )}
    </div>
  )
}

// Pros: Immediate feedback, great for password strength
// Cons: Can be annoying for some fields

⏰ Validation Timing Best Practices

Use on submit for: Overall form validation
Use on blur for: Most text fields (email, name, etc.)
Use real-time for: Password strength, username availability

Combine approaches: Real-time for some fields, blur for others!

Common Validation Patterns

Required Field

pattern-required.jsx
const validateRequired = (value, fieldName) => {
  if (!value || value.trim() === '') {
    return `${fieldName} is required`
  }
  return ''
}

// Usage
const nameError = validateRequired(name, 'Name')
const emailError = validateRequired(email, 'Email')

Email Format

pattern-email.jsx
const validateEmail = (email) => {
  if (!email) {
    return 'Email is required'
  }
  
  // Simple email regex
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  
  if (!emailRegex.test(email)) {
    return 'Please enter a valid email'
  }
  
  return ''
}

// More comprehensive version
const validateEmailAdvanced = (email) => {
  if (!email) return 'Email is required'
  
  if (email.length > 254) {
    return 'Email is too long'
  }
  
  const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
  
  if (!emailRegex.test(email)) {
    return 'Please enter a valid email address'
  }
  
  return ''
}

Password Strength

pattern-password.jsx
const validatePassword = (password) => {
  const errors = []
  
  if (password.length < 8) {
    errors.push('Must be at least 8 characters')
  }
  
  if (!/[a-z]/.test(password)) {
    errors.push('Must contain a lowercase letter')
  }
  
  if (!/[A-Z]/.test(password)) {
    errors.push('Must contain an uppercase letter')
  }
  
  if (!/[0-9]/.test(password)) {
    errors.push('Must contain a number')
  }
  
  if (!/[!@#$%^&*]/.test(password)) {
    errors.push('Must contain a special character (!@#$%^&*)')
  }
  
  return errors
}

// Returns array of errors (empty if valid)

Matching Fields

pattern-match.jsx
const validatePasswordMatch = (password, confirmPassword) => {
  if (!confirmPassword) {
    return 'Please confirm your password'
  }
  
  if (password !== confirmPassword) {
    return 'Passwords do not match'
  }
  
  return ''
}

// Usage in component
function PasswordForm() {
  const [password, setPassword] = React.useState('')
  const [confirmPassword, setConfirmPassword] = React.useState('')
  const [error, setError] = React.useState('')
  
  const handleConfirmBlur = () => {
    setError(validatePasswordMatch(password, confirmPassword))
  }
  
  return (
    <div>
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <input
        type="password"
        value={confirmPassword}
        onChange={(e) => setConfirmPassword(e.target.value)}
        onBlur={handleConfirmBlur}
        placeholder="Confirm Password"
      />
      {error && <p className="error">{error}</p>}
    </div>
  )
}

Length Validation

pattern-length.jsx
const validateLength = (value, min, max, fieldName) => {
  if (value.length < min) {
    return `${fieldName} must be at least ${min} characters`
  }
  
  if (value.length > max) {
    return `${fieldName} must be less than ${max} characters`
  }
  
  return ''
}

// Usage
const usernameError = validateLength(username, 3, 20, 'Username')
const bioError = validateLength(bio, 0, 500, 'Bio')

Number Range

pattern-range.jsx
const validateRange = (value, min, max, fieldName) => {
  const num = Number(value)
  
  if (isNaN(num)) {
    return `${fieldName} must be a number`
  }
  
  if (num < min) {
    return `${fieldName} must be at least ${min}`
  }
  
  if (num > max) {
    return `${fieldName} must be at most ${max}`
  }
  
  return ''
}

// Usage
const ageError = validateRange(age, 0, 120, 'Age')
const quantityError = validateRange(quantity, 1, 100, 'Quantity')

Complete Validation Example

complete-validation.jsx
function RegistrationForm() {
  const [formData, setFormData] = React.useState({
    username: '',
    email: '',
    password: '',
    confirmPassword: '',
    age: ''
  })
  
  const [errors, setErrors] = React.useState({})
  const [touched, setTouched] = React.useState({})
  const [isSubmitting, setIsSubmitting] = React.useState(false)
  
  // Validation functions
  const validateField = (name, value) => {
    switch (name) {
      case 'username':
        if (!value) return 'Username is required'
        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) return 'Email is required'
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
          return 'Please enter a valid email'
        }
        return ''
        
      case 'password':
        if (!value) return 'Password is required'
        if (value.length < 8) return 'Password must be at least 8 characters'
        if (!/[A-Z]/.test(value)) return 'Password must contain an uppercase letter'
        if (!/[0-9]/.test(value)) return 'Password must contain a number'
        return ''
        
      case 'confirmPassword':
        if (!value) return 'Please confirm your password'
        if (value !== formData.password) return 'Passwords do not match'
        return ''
        
      case 'age':
        if (!value) return 'Age is required'
        const age = Number(value)
        if (isNaN(age)) return 'Age must be a number'
        if (age < 13) return 'You must be at least 13 years old'
        if (age > 120) return 'Please enter a valid age'
        return ''
        
      default:
        return ''
    }
  }
  
  const handleChange = (e) => {
    const { name, value } = e.target
    setFormData({ ...formData, [name]: value })
    
    // Validate if field was touched
    if (touched[name]) {
      setErrors({ ...errors, [name]: validateField(name, value) })
    }
  }
  
  const handleBlur = (e) => {
    const { name, value } = e.target
    setTouched({ ...touched, [name]: true })
    setErrors({ ...errors, [name]: validateField(name, value) })
  }
  
  const handleSubmit = async (e) => {
    e.preventDefault()
    
    // Validate all fields
    const newErrors = {}
    Object.keys(formData).forEach(key => {
      const error = validateField(key, formData[key])
      if (error) newErrors[key] = error
    })
    
    setErrors(newErrors)
    
    // Stop if there are errors
    if (Object.keys(newErrors).length > 0) {
      return
    }
    
    // Submit form
    setIsSubmitting(true)
    
    try {
      await fetch('/api/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(formData)
      })
      
      console.log('Registration successful!')
    } catch (error) {
      console.error('Registration failed:', error)
    } finally {
      setIsSubmitting(false)
    }
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          name="username"
          value={formData.username}
          onChange={handleChange}
          onBlur={handleBlur}
          placeholder="Username"
        />
        {touched.username && errors.username && (
          <p className="error">{errors.username}</p>
        )}
      </div>
      
      <div>
        <input
          name="email"
          type="email"
          value={formData.email}
          onChange={handleChange}
          onBlur={handleBlur}
          placeholder="Email"
        />
        {touched.email && errors.email && (
          <p className="error">{errors.email}</p>
        )}
      </div>
      
      <div>
        <input
          name="password"
          type="password"
          value={formData.password}
          onChange={handleChange}
          onBlur={handleBlur}
          placeholder="Password"
        />
        {touched.password && errors.password && (
          <p className="error">{errors.password}</p>
        )}
      </div>
      
      <div>
        <input
          name="confirmPassword"
          type="password"
          value={formData.confirmPassword}
          onChange={handleChange}
          onBlur={handleBlur}
          placeholder="Confirm Password"
        />
        {touched.confirmPassword && errors.confirmPassword && (
          <p className="error">{errors.confirmPassword}</p>
        )}
      </div>
      
      <div>
        <input
          name="age"
          type="number"
          value={formData.age}
          onChange={handleChange}
          onBlur={handleBlur}
          placeholder="Age"
        />
        {touched.age && errors.age && (
          <p className="error">{errors.age}</p>
        )}
      </div>
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Registering...' : 'Register'}
      </button>
    </form>
  )
}

Interactive Demo

ReactPlayground.jsx
Code Editor
Preview

💡 Tip: Edit the code above and click "Run" to see your changes

Displaying Error Messages

Inline Errors

display-inline.jsx
function Field({ label, error, ...inputProps }) {
  return (
    <div className="field">
      <label>{label}</label>
      <input
        {...inputProps}
        className={error ? 'input-error' : 'input'}
      />
      {error && <span className="error-message">{error}</span>}
    </div>
  )
}

// CSS
.input-error {
  border-color: red;
}

.error-message {
  color: red;
  font-size: 12px;
  margin-top: 4px;
}

Summary Errors

display-summary.jsx
function Form() {
  const [errors, setErrors] = React.useState({})
  
  const errorMessages = Object.values(errors).filter(Boolean)
  
  return (
    <form>
      {errorMessages.length > 0 && (
        <div className="error-summary">
          <h3>Please fix the following errors:</h3>
          <ul>
            {errorMessages.map((error, i) => (
              <li key={i}>{error}</li>
            ))}
          </ul>
        </div>
      )}
      
      {/* Form fields */}
    </form>
  )
}

Success Messages

display-success.jsx
function Field({ value, error, touched, validMessage }) {
  const isValid = touched && !error && value
  
  return (
    <div>
      <input value={value} />
      {error && touched && (
        <span className="error">❌ {error}</span>
      )}
      {isValid && (
        <span className="success">✓ {validMessage}</span>
      )}
    </div>
  )
}

Validation Best Practices

  • Validate on blur for most fields - Don't annoy users while typing
  • Use real-time for password strength - Immediate feedback helps
  • Always validate on submit - Final check before submission
  • Show errors after user interaction - Use touched state
  • Be specific in error messages - "Password must contain a number" not "Invalid password"
  • Disable submit during validation - Prevent double submission
  • Clear errors on successful submit - Reset form state
  • Use consistent styling - Red for errors, green for success
  • Validate on the server too - Never trust client-side only!
  • Consider accessibility - Use aria attributes for screen readers

Client-Side Validation is NOT Enough!

Client-side validation improves user experience, but it can be bypassed. Always validate on the server too! Users can:
• Disable JavaScript
• Modify client-side code
• Send requests directly to your API

Client-side = UX. Server-side = Security.

Key Takeaways

  • Validation ensures users provide correct data
  • Validate on submit for basic validation
  • Validate on blur for better user experience
  • Use real-time validation for password strength
  • Store errors in state (object for multiple fields)
  • Track "touched" state to avoid showing errors too early
  • Common validations: required, email, password, length, range
  • Display errors inline near the relevant field
  • Be specific and helpful in error messages
  • Show success indicators for valid fields
  • Disable submit button during submission
  • Always validate on the server too!
  • Validation improves data quality and user experience

What's Next?

Congratulations! You've completed the Events and Interactivity section. You now know:

  • How to handle user interactions with events
  • How to work with event objects and prevent defaults
  • How to build controlled forms in React
  • How to validate forms and display helpful errors

You have all the tools to build fully interactive, user-friendly forms and applications! But there's still so much more to learn:

  • Side Effects - API calls, timers, and more
  • Custom Hooks - Reusable logic patterns
  • Context API - Sharing state across components
  • Performance - Optimization techniques
  • And much more!

Keep building - you're becoming a React expert! 🎉

Learning how to build bulletproof forms with validation in React!

Previous
Forms in React
Next
Introduction to useEffect

Never Miss a New React Tutorial

Join 2,000+ developers learning React step-by-step. Get new tutorials, tips, and exclusive resources delivered to your inbox - completely FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

React Tutorials

0 of 42 completed

Your Progress0%

Topics

Getting Started

  • What is React?
  • React vs Vanilla JavaScript
  • Setting Up Your Environment
  • Your First React App

JSX Fundamentals

  • Introduction to JSX
  • JSX Expressions and Variables
  • Conditional Rendering
  • Lists and Keys
  • Styling in JSX

Components Basics

  • Function Components
  • Props
  • Children Prop
  • Props Destructuring
  • Component Composition

State Management

  • Introduction to State
  • useState Hook
  • State Updates and Re-renders
  • Multiple State Variables
  • State Best Practices

Events and Interactivity

  • Handling Events
  • Event Objects
  • Forms in React
  • Form Validation

Side Effects and Data

  • Introduction to useEffect
  • Data Fetching
  • Cleanup Functions
  • Dependency Arrays

Advanced Hooks

  • useRef Hook
  • useContext Hook
  • useMemo Hook
  • useCallback Hook

React Patterns

  • Custom Hooks
  • Higher-Order Components
  • Render Props Pattern

React Router

  • Introduction to React Router
  • Routes and Navigation
  • Dynamic Routes and Parameters

Best Practices

  • Component Organization
  • Performance Optimization
  • Error Handling
  • Testing React Components

Real World Project

  • Building a Complete App
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo