Forms and Validation
Level: Beginner
- Controlled inputs and two-way binding
- Handling multiple form inputs with state
- Form submission and preventing default behavior
- Validation patterns: required, email, length, custom rules
- Displaying validation errors to users
- Clearing forms after submission
- Form libraries (Formik, React Hook Form)
- Building SMS student registration, teacher admission, fee payment forms
Why This Matters
Forms and Validation is part of building maintainable React applications. You will use it when creating student dashboards, forms, tables, API-connected screens, routing flows, and reusable UI components.
Forms collect data from users. React provides patterns for handling inputs and validation.
The Problem
Beginners often write React code that works for a small demo but becomes difficult when data, forms, API calls, and reusable components grow. This lesson explains Forms and Validation in a way that helps you build predictable UI for real .NET Web API projects.
Controlled vs Uncontrolled Inputs
Controlled Inputs
Value is controlled by React state. Every keystroke updates state.
function StudentNameInput() {
const [name, setName] = useState("");
const handleChange = (e) => {
setName(e.target.value);
};
return (
<div>
<input
type="text"
value={name}
onChange={handleChange}
placeholder="Enter name"
/>
<p>You typed: {name}</p>
</div>
);
}
Pros: Full control, easy validation, can respond to input changes Cons: More code, state updates on every keystroke
Uncontrolled Inputs
Access value from DOM directly (like HTML forms).
function StudentNameForm() {
const inputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const name = inputRef.current.value;
console.log("Submitted:", name);
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} type="text" placeholder="Enter name" />
<button type="submit">Submit</button>
</form>
);
}
Pros: Less code, simpler Cons: No validation, harder to respond to input changes
Use controlled inputs for most React forms.
Basic Form with Multiple Inputs
function StudentRegistration() {
const [formData, setFormData] = useState({
name: "",
email: "",
className: "10",
parentName: "",
parentPhone: ""
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("Form submitted:", formData);
// Send to API
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Student Name"
required
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<select
name="className"
value={formData.className}
onChange={handleChange}
>
<option value="10">Class 10</option>
<option value="11">Class 11</option>
<option value="12">Class 12</option>
</select>
<input
type="text"
name="parentName"
value={formData.parentName}
onChange={handleChange}
placeholder="Parent Name"
/>
<input
type="tel"
name="parentPhone"
value={formData.parentPhone}
onChange={handleChange}
placeholder="Parent Phone"
/>
<button type="submit">Register</button>
</form>
);
}
Form Validation
Real-Time Validation
Validate as user types:
function StudentEmailInput() {
const [email, setEmail] = useState("");
const [error, setError] = useState("");
const handleChange = (e) => {
const value = e.target.value;
setEmail(value);
// Validate
if (value && !value.includes("@")) {
setError("Email must contain @");
} else {
setError("");
}
};
return (
<div>
<input
type="email"
value={email}
onChange={handleChange}
placeholder="Enter email"
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
}
Submit-Time Validation
Validate when form is submitted:
function StudentRegistration() {
const [formData, setFormData] = useState({
name: "",
email: "",
phone: ""
});
const [errors, setErrors] = useState({});
const validateForm = () => {
const newErrors = {};
if (!formData.name.trim()) {
newErrors.name = "Name is required";
}
if (!formData.email.includes("@")) {
newErrors.email = "Valid email required";
}
if (formData.phone.length < 10) {
newErrors.phone = "Phone must be 10+ digits";
}
return newErrors;
};
const handleSubmit = (e) => {
e.preventDefault();
const newErrors = validateForm();
if (Object.keys(newErrors).length === 0) {
// No errors, submit
console.log("Submitting:", formData);
} else {
// Has errors, show them
setErrors(newErrors);
}
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
{errors.name && <p style={{ color: 'red' }}>{errors.name}</p>}
<input
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <p style={{ color: 'red' }}>{errors.email}</p>}
<input
name="phone"
value={formData.phone}
onChange={handleChange}
placeholder="Phone"
/>
{errors.phone && <p style={{ color: 'red' }}>{errors.phone}</p>}
<button type="submit">Register</button>
</form>
);
}
Form with Checkboxes and Radios
function StudentPreferences() {
const [preferences, setPreferences] = useState({
sports: false,
music: false,
arts: false,
hostel: "day",
newsletter: true
});
const handleCheckChange = (e) => {
const { name, checked } = e.target;
setPreferences({
...preferences,
[name]: checked
});
};
const handleRadioChange = (e) => {
const { name, value } = e.target;
setPreferences({
...preferences,
[name]: value
});
};
return (
<form>
<h3>Activities</h3>
<label>
<input
type="checkbox"
name="sports"
checked={preferences.sports}
onChange={handleCheckChange}
/>
Sports
</label>
<label>
<input
type="checkbox"
name="music"
checked={preferences.music}
onChange={handleCheckChange}
/>
Music
</label>
<label>
<input
type="checkbox"
name="arts"
checked={preferences.arts}
onChange={handleCheckChange}
/>
Arts
</label>
<h3>Accommodation</h3>
<label>
<input
type="radio"
name="hostel"
value="day"
checked={preferences.hostel === "day"}
onChange={handleRadioChange}
/>
Day Scholar
</label>
<label>
<input
type="radio"
name="hostel"
value="hostel"
checked={preferences.hostel === "hostel"}
onChange={handleRadioChange}
/>
Hostel
</label>
<label>
<input
type="checkbox"
name="newsletter"
checked={preferences.newsletter}
onChange={handleCheckChange}
/>
Subscribe to newsletter
</label>
<button type="submit">Save Preferences</button>
</form>
);
}
Common Validation Rules
const validateEmail = (email) => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
};
const validatePhone = (phone) => {
return phone.length === 10 && !isNaN(phone);
};
const validateName = (name) => {
return name.trim().length > 0 && name.length < 100;
};
const validateAge = (age) => {
const num = parseInt(age);
return num >= 5 && num <= 100;
};
// Usage
if (!validateEmail(email)) {
setErrors({ ...errors, email: "Invalid email" });
}
Complete Registration Form Example
function TeacherRegistration() {
const [formData, setFormData] = useState({
name: "",
email: "",
employeeCode: "",
subject: "",
qualification: "",
experienceYears: "",
salary: ""
});
const [errors, setErrors] = useState({});
const [submitted, setSubmitted] = useState(false);
const validate = () => {
const newErrors = {};
if (!formData.name) newErrors.name = "Name required";
if (!formData.email.includes("@")) newErrors.email = "Valid email required";
if (!formData.employeeCode) newErrors.employeeCode = "Code required";
if (!formData.subject) newErrors.subject = "Subject required";
if (!formData.qualification) newErrors.qualification = "Qualification required";
if (!formData.experienceYears) newErrors.experienceYears = "Experience required";
if (!formData.salary || formData.salary < 0) newErrors.salary = "Valid salary required";
return newErrors;
};
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({ ...formData, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length === 0) {
// Submit to API
fetch("/api/teachers", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData)
})
.then(r => r.json())
.then(data => {
setSubmitted(true);
setFormData({
name: "", email: "", employeeCode: "",
subject: "", qualification: "", experienceYears: "", salary: ""
});
})
.catch(err => setErrors({ submit: err.message }));
} else {
setErrors(newErrors);
}
};
if (submitted) {
return <div className="success">Registration submitted successfully!</div>;
}
return (
<form onSubmit={handleSubmit}>
<div className="form-group">
<input
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Full Name"
/>
{errors.name && <p className="error">{errors.name}</p>}
</div>
<div className="form-group">
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <p className="error">{errors.email}</p>}
</div>
<div className="form-group">
<input
name="employeeCode"
value={formData.employeeCode}
onChange={handleChange}
placeholder="Employee Code"
/>
{errors.employeeCode && <p className="error">{errors.employeeCode}</p>}
</div>
<div className="form-group">
<select
name="subject"
value={formData.subject}
onChange={handleChange}
>
<option value="">Select Subject</option>
<option value="Mathematics">Mathematics</option>
<option value="Science">Science</option>
<option value="English">English</option>
<option value="Social">Social Studies</option>
</select>
{errors.subject && <p className="error">{errors.subject}</p>}
</div>
<div className="form-group">
<input
name="qualification"
value={formData.qualification}
onChange={handleChange}
placeholder="Qualification (e.g., M.Sc, B.Ed)"
/>
{errors.qualification && <p className="error">{errors.qualification}</p>}
</div>
<div className="form-group">
<input
type="number"
name="experienceYears"
value={formData.experienceYears}
onChange={handleChange}
placeholder="Years of Experience"
/>
{errors.experienceYears && <p className="error">{errors.experienceYears}</p>}
</div>
<div className="form-group">
<input
type="number"
name="salary"
value={formData.salary}
onChange={handleChange}
placeholder="Salary"
/>
{errors.salary && <p className="error">{errors.salary}</p>}
</div>
{errors.submit && <p className="error">{errors.submit}</p>}
<button type="submit">Register Teacher</button>
</form>
);
}
Key Takeaways
- Use controlled inputs for React forms
- Validate on submit or as user types
- Show error messages next to fields
- Handle form submission with
preventDefault() - Next: Conditional rendering
- Forgetting preventDefault() — Form reloads page
- Not validating — Invalid data goes to backend
- Uncontrolled inputs — Can't validate or respond
- Not clearing form after submit — Confuses users
For complex validation, use libraries like formik or react-hook-form. But start with manual validation to understand how it works.
Use ChatGPT, Claude, or Copilot to go deeper on Forms and Validation. Try these prompts:
"What's the difference between controlled and uncontrolled inputs?""When should you validate: on change or on submit?""How do you handle checkboxes in React forms?""Quiz me on React forms"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Quick Definitions
- Forms and Validation - The main React concept explained in this lesson.
- Component - A reusable function that returns UI.
- Props/state/effects - Core React ideas used to pass data, remember data, and run side effects.
- Real project usage - How this appears in forms, dashboards, routes, and API-connected pages.
Common Mistakes
- Copying React code without understanding data flow
- Mutating arrays or objects directly instead of creating new values
- Forgetting keys, dependencies, loading states, or error states where needed
- Putting too much logic in one component
- Not testing the screen with realistic School Management System data
Practice Task
Create a small React example using Forms and Validation. Keep it focused on one School Management System screen.
Suggested practice:
- Build a small component or page for students, attendance, marks, or fees.
- Pass realistic data into the component.
- Add one success state and one empty/error state where relevant.
- Explain the data flow in your own words.
- Rebuild the same example once without looking at the article.
Quick Revision
| Question | Answer |
|---|---|
| What is the main idea? | Understand and apply Forms and Validation in React. |
| Where is it used? | Student dashboards, forms, tables, routes, and API-connected screens. |
| What should beginners focus on? | Clear components, predictable data flow, and small examples. |
| What is the best debugging habit? | Check props, state, render output, and browser console step by step. |
Forms and Validation is a React concept used to build clear, reusable, and predictable user interfaces. I would explain the problem it solves, show a small component example, and mention a common mistake beginners should avoid.
It is used in screens like student lists, admission forms, attendance dashboards, marks reports, routing pages, and API-connected admin panels.