Skip to main content

Exercise 2: Form Handling and Validation

Objective

Master controlled components, form handling, and validation in React.

Task

Build a complete registration form with validation.

Requirements

1. Form Fields

  • Username (3-20 characters, alphanumeric only)
  • Email (valid email format)
  • Password (min 8 chars, must include uppercase, lowercase, number, special char)
  • Confirm Password (must match password)
  • Date of Birth (must be 18+)
  • Country (dropdown select)
  • Terms and Conditions (checkbox, required)

2. Validation Rules

  • Real-time validation on blur
  • Show errors only after field is touched
  • Disable submit if form is invalid
  • Clear validation on field focus
  • Display success message on valid submission

3. Features

  • All fields controlled by React state
  • Custom validation messages
  • Show/hide password toggle
  • Password strength indicator
  • Form submission handling
  • Reset form after submission

Implementation

interface FormData {
username: string;
email: string;
password: string;
confirmPassword: string;
dateOfBirth: string;
country: string;
acceptedTerms: boolean;
}

interface FormErrors {
[key: string]: string;
}

function RegistrationForm() {
// Your implementation here
}

Validation Functions

const validateUsername = (username: string): string => {
if (!username) return 'Username is required';
if (username.length < 3) return 'Username must be at least 3 characters';
if (username.length > 20) return 'Username must be less than 20 characters';
if (!/^[a-zA-Z0-9]+$/.test(username)) return 'Username must be alphanumeric';
return '';
};

const validateEmail = (email: string): string => {
if (!email) return 'Email is required';
if (!/\S+@\S+\.\S+/.test(email)) return 'Email is invalid';
return '';
};

// Implement remaining validators...

Password Strength Indicator

type PasswordStrength = 'weak' | 'medium' | 'strong';

const getPasswordStrength = (password: string): PasswordStrength => {
let strength = 0;
if (password.length >= 8) strength++;
if (/[a-z]/.test(password) && /[A-Z]/.test(password)) strength++;
if (/\d/.test(password)) strength++;
if (/[^a-zA-Z0-9]/.test(password)) strength++;

if (strength <= 1) return 'weak';
if (strength <= 3) return 'medium';
return 'strong';
};

Expected UI

function App() {
return (
<div className='container'>
<h1>Create Account</h1>
<RegistrationForm onSubmit={data => console.log(data)} />
</div>
);
}

Bonus Challenges

  1. Add async email availability check (simulate API call)
  2. Implement auto-focus on first error field
  3. Add "Sign in with Google" button (UI only)
  4. Save form progress to localStorage
  5. Add phone number field with country code selector
  6. Implement multi-step form (wizard)

Testing Checklist

  • All validation rules work correctly
  • Errors show only after field is touched
  • Submit button is disabled when form is invalid
  • Password strength indicator updates correctly
  • Passwords must match
  • Age validation works (must be 18+)
  • Form resets after successful submission
  • Terms checkbox is required

Time Estimate

3-4 hours

Hints

  • Create a custom useForm hook to manage state
  • Use a touched state object to track which fields have been interacted with
  • Extract validation logic into separate functions
  • Consider using a validation library for bonus (Yup, Zod)