Build and manage controlled forms in React with inputs, validation, and submit handlers.
const [email, setEmail] = useState("");
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
/>const [form, setForm] = useState({ name: "", email: "" });
const handleChange = (e) => {
const { name, value } = e.target;
setForm(prev => ({ ...prev, [name]: value }));
};name attribute to identify which input changed.checked instead of value in state.const [agree, setAgree] = useState(false);
<input
type="checkbox"
checked={agree}
onChange={(e) => setAgree(e.target.checked)}
/>const [role, setRole] = useState("user");
<select value={role} onChange={(e) => setRole(e.target.value)}>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>value attribute.const [email, setEmail] = useState("");
const [error, setError] = useState("");
const handleChange = (e) => {
const value = e.target.value;
setEmail(value);
if (!value.includes("@")) setError("Invalid email");
else setError("");
};<button disabled={error}>Submit</button>const handleSubmit = (e) => {
e.preventDefault();
console.log(form);
};
<form onSubmit={handleSubmit}>
{/* inputs here */}
<button type="submit">Submit</button>
</form>setForm({}).Add a name attribute to each input that matches the corresponding state key, then use a single handler: setState(prev => ({ ...prev, [e.target.name]: e.target.value })). This one function scales to any number of fields.
You're missing e.preventDefault() at the top of your submit handler. Without it, the browser performs its default form submission, which reloads the page and wipes your component state.
Checkboxes use e.target.checked instead of e.target.value. In a shared handler, check e.target.type === 'checkbox' and use the appropriate property: type === 'checkbox' ? e.target.checked : e.target.value.
Validate on submit for simple forms to avoid showing errors before the user finishes typing. For better UX, validate on onBlur (when the field loses focus) so errors appear after the user leaves a field rather than mid-input.
A controlled input stores its value in React state and reads it back via the value prop, making React the single source of truth. An uncontrolled input stores its value in the DOM and is read via a ref; controlled inputs are preferred because they make validation and dynamic updates straightforward.