Kodokon kodokon.com

Controlled forms

Learn to drive every form field from React state so you can validate and submit reliable data.

8 min · 3 questions

Open this lesson in Kodokon

In React, a form becomes predictable when every field is controlled: the displayed value comes from the component's state, and every keystroke goes through onChange to update that state. The DOM is no longer the source of truth - your state is. In practice, you bind the value prop to a state variable and provide a handler that calls the update function.

JSX
import { useState } from "react";

function EmailField() {
  const [email, setEmail] = useState("");

  return (
    <input
      type="email"
      value={email}
      onChange={(e) => setEmail(e.target.value)}
    />
  );
}
A controlled field: value reads the state, onChange writes it.

A real form rarely has a single field. Rather than stacking up useState calls, group the values into one object and write a generic handler. Each field's name attribute must match a key of the object exactly: that is what tells the handler which property to update.

JSX
const [form, setForm] = useState({
  username: "",
  password: "",
});

function handleChange(e) {
  const { name, value } = e.target;
  setForm((prev) => ({ ...prev, [name]: value }));
}
A single handler for every field in the form.

That leaves submission. By default, the browser reloads the page when a form is submitted: call e.preventDefault() at the top of your onSubmit handler. It is also the right place to validate the data before sending it, since the entire form state is already at hand.

JSX
function handleSubmit(e) {
  e.preventDefault();
  if (form.username.trim() === "") {
    alert("Username is required.");
    return;
  }
  console.log("Submitting:", form);
}

return (
  <form onSubmit={handleSubmit}>
    <input
      name="username"
      value={form.username}
      onChange={handleChange}
    />
    <button type="submit">Create account</button>
  </form>
);
Validate, then submit, without reloading the page.

Knowledge check

Make sure you remember the key points of this lesson.

  1. What defines a controlled field in React?
    • Its value is stored in the DOM and read with a ref
    • Its value comes from state and every keystroke goes through onChange
    • It is read-only until the form is submitted
    • It automatically validates the data you type
  2. You pass value={email} to an <input> without providing an onChange. What happens?
    • The field freezes: typing has no effect anymore
    • The field works normally, React handles the input on its own
    • The component crashes on the first render
  3. What is e.preventDefault() for in the onSubmit handler?
    • To prevent the page reload triggered by native form submission
    • To automatically clear the form fields
    • To stop the event from propagating to parent components
    • To disable the browser's HTML validation