16 minLesson 19 of 40
React Fundamentals
Forms in React
Forms in React
React forms use controlled components — React state is the single source of truth for every input value. This makes validation, conditional logic, and form submission clean and predictable.
Controlled Inputs
function EmailInput() {
const [email, setEmail] = useState("");
return (
<input
type="email"
value={email} // controlled by state
onChange={e => setEmail(e.target.value)} // update state on change
placeholder="Enter email"
/>
);
}
Multi-Field Form
function SignupForm() {
const [form, setForm] = useState({
name: "",
email: "",
password: "",
role: "user",
terms: false
});
// Generic handler for all fields
function handleChange(e) {
const { name, value, type, checked } = e.target;
setForm(prev => ({
...prev,
[name]: type === "checkbox" ? checked : value
}));
}
return (
<form>
<input name="name" value={form.name} onChange={handleChange} />
<input name="email" type="email" value={form.email} onChange={handleChange} />
<input name="password" type="password" value={form.password} onChange={handleChange} />
<select name="role" value={form.role} onChange={handleChange}>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
<input name="terms" type="checkbox" checked={form.terms} onChange={handleChange} />
</form>
);
}
Validation
function RegisterForm() {
const [form, setForm] = useState({ username: "", email: "", password: "" });
const [errors, setErrors] = useState({});
const [submitted, setSubmitted] = useState(false);
function validate(data) {
const newErrors = {};
if (!data.username.trim()) newErrors.username = "Username is required";
else if (data.username.length < 3) newErrors.username = "Must be 3+ characters";
if (!data.email.includes("@")) newErrors.email = "Valid email required";
if (data.password.length < 8) newErrors.password = "Must be 8+ characters";
return newErrors;
}
function handleChange(e) {
const { name, value } = e.target;
setForm(prev => ({ ...prev, [name]: value }));
// Clear error on change
if (errors[name]) setErrors(prev => ({ ...prev, [name]: "" }));
}
async function handleSubmit(e) {
e.preventDefault();
const newErrors = validate(form);
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
try {
await registerUser(form);
setSubmitted(true);
} catch (err) {
setErrors({ submit: err.message });
}
}
if (submitted) return <p>Registration successful!</p>;
return (
<form onSubmit={handleSubmit} noValidate>
<div className="field">
<label>Username</label>
<input name="username" value={form.username} onChange={handleChange}
className={errors.username ? "error" : ""} />
{errors.username && <span className="error-msg">{errors.username}</span>}
</div>
<div className="field">
<label>Email</label>
<input name="email" type="email" value={form.email} onChange={handleChange}
className={errors.email ? "error" : ""} />
{errors.email && <span className="error-msg">{errors.email}</span>}
</div>
<div className="field">
<label>Password</label>
<input name="password" type="password" value={form.password} onChange={handleChange}
className={errors.password ? "error" : ""} />
{errors.password && <span className="error-msg">{errors.password}</span>}
</div>
{errors.submit && <div className="error-banner">{errors.submit}</div>}
<button type="submit">Create Account</button>
</form>
);
}
Uncontrolled Inputs with useRef
Sometimes you don't need React to track every keystroke — use useRef for simpler cases:
import { useRef } from "react";
function SearchBox({ onSearch }) {
const inputRef = useRef(null);
function handleSubmit(e) {
e.preventDefault();
onSearch(inputRef.current.value);
}
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} type="search" placeholder="Search..." />
<button>Search</button>
</form>
);
}
Use react-hook-form or formik for complex production forms — they handle validation, touched state, and submission with less boilerplate.
Next lesson: React Router v6 — client-side navigation in single-page apps.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises