Skip to content

Repository files navigation

⌨ Controlled vs Uncontrolled — who owns the input's value, React or the DOM?

An interactive, side-by-side demo of the two kinds of React inputs, with live render counters that make the difference obvious.

▶ Live: https://controlled-vs-uncontrolled-kappa.vercel.app/

Every React <input> is one of two things, and the whole distinction is: who owns the value?

Controlled — React owns it

const [name, setName] = useState("");
<input value={name} onChange={(e) => setName(e.target.value)} />

The value lives in state. Every keystroke fires onChangesetName → a re-render. Because React always has the current value, you can validate, format, or transform it live (the demo has an "UPPERCASE as you type" toggle). Type "abcd" and the render counter goes 1 → 5.

Uncontrolled — the DOM owns it

const ref = useRef(null);
<input defaultValue="" ref={ref} />
// read on demand: ref.current.value

The value lives in the DOM node. Typing does not re-render React — the counter stays put no matter how much you type. You read the value with a ref when you actually need it (e.g. on submit). defaultValue sets the initial value only; after that the DOM is in charge.

The warning everyone hits

Warning: A component is changing a controlled input to be uncontrolled.

It fires when an input's value flips between a real string and undefined/null across renders — usually because state started undefined (useState() with no argument, or value={data?.name} before data loads). Fix: always pass a defined value.

const [name, setName] = useState("");   // not useState()
<input value={name ?? ""} onChange={...} />

Which one?

  • Controlled — when you need the value while typing: live validation, character count, formatting, enabling/disabling submit, fields that depend on each other. Default to this.
  • Uncontrolled — simple forms you read on submit, integrating a non-React widget, or <input type="file"> (always uncontrolled). Form libraries like React Hook Form use refs (uncontrolled) under the hood to avoid re-rendering on every keystroke.

Stack

React 19 · TypeScript · Vite.

Run locally

npm install
npm run dev

License

MIT © 2026 dev48vdev48v.infy.uk

About

Interactive side-by-side of controlled vs uncontrolled React inputs — live render counters show React owning the value (re-render per keystroke) vs the DOM owning it (no re-render, read via ref), plus the classic controlled/uncontrolled warning and when to use each. React 19 + TS + Vite.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages