DevPath · Learn to code ESPTEN

Events and forms

Controlled inputs

The single source of truth

An HTML <input> stores its own value internally. In React we prefer that value to live in the component's state: that way the state is the single source of truth and the UI always reflects exactly what's stored.

A controlled input combines two things:

  1. value={x} — the input displays the value from state.
  2. onChange={e => setX(e.target.value)} — every time the user types, we update the state with e.target.value (the input's current text).
function Echo() {
  const [text, setText] = useState("");
  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <p>You typed: {text}</p>
    </div>
  );
}

The flow is a closed loop: the user types → onChange fires → setText changes the state → React re-renders → the input shows the new value. The input never "decides" on its own: it only reflects the state.

Why it's useful

Since the value is in the state, you can read it, transform it, or validate it at any time: count characters, uppercase it, disable a button if it's empty... all without touching the DOM directly.

<input
  value={name}
  onChange={(e) => setName(e.target.value.toUpperCase())}
/>

Rule of thumb: if you set value={...} you must also set onChange. Without onChange, the input would be "frozen" and the user couldn't type.

Put this into practice

DevPath is a hands-on course: you read the theory here; in the app you put it into practice with exercises that really run, offline.

Start free in the app →
← Handling eventsForms →