In React, what makes a form input controlled versus uncontrolled, and what is the difference between the value and defaultValue props?
answer
- who owns the value
- React state or the DOM node
- value plus onChange versus defaultValue
- defaultValue only seeds the first mount
- read it later with a ref
basics
~20 sA controlled input's displayed value comes from React state through the value prop and changes only when onChange updates that state. An uncontrolled input keeps its value inside the DOM node, seeded once by defaultValue, and you read it on demand.
solid answer
~50 sThe question is which side owns the value. A **controlled** input gets `value={something}` from state and an `onChange` that writes the new value back, so React state is the single source of truth and every keystroke round-trips through a re-render. An **uncontrolled** input has no `value` prop; the DOM node holds the text, and `defaultValue` (or `defaultChecked` for checkboxes and radios) only seeds it on the first mount — changing that prop later does nothing. You read an uncontrolled field when you need it, typically through a ref or by constructing `FormData` from the form element. Passing `value` with no `onChange` and no `readOnly` freezes the field and React warns about it, which is the mistake that usually surfaces this distinction. Most real forms mix both: controlled where the value must be validated, formatted or mirrored elsewhere as you type, uncontrolled where you only need it at submit time.
code
jsx · 20 linesimport { useRef, useState } from 'react';
export function BothKinds() {
const [controlled, setControlled] = useState('');
const uncontrolledRef = useRef(null);
return (
<form
onSubmit={(e) => {
e.preventDefault();
console.log('controlled:', controlled);
console.log('uncontrolled:', uncontrolledRef.current.value);
}}
>
<input value={controlled} onChange={(e) => setControlled(e.target.value)} />
<input ref={uncontrolledRef} defaultValue="Ada" />
<button type="submit">Send</button>
</form>
);
}go deeper
Be ready to say plainly that a controlled input needs both value and onChange, that an uncontrolled one uses defaultValue, and that checkboxes use checked and defaultChecked instead.
Explain the mechanics: the keystroke round-trips through state before it reaches the screen, defaultValue applies only at mount, and value without onChange triggers React's read-only warning.
Show judgment about which fields deserve state at all, and be able to argue that a keystroke re-render is a structural question about where state lives rather than an inherent cost of controlled inputs.
Own the convention for the codebase: which pattern is the default, how it interacts with the form library in use, and how you keep teams from spreading form state across components that do not need it.
## The one question that decides everything A text field has a value, and that value has to live somewhere. Either React state holds it and the DOM element merely displays it, or the DOM element holds it and React never tracks it. That is the whole controlled/uncontrolled distinction; every other detail follows from it. ## Controlled: React state is the source of truth ```jsx function NameField() { const [name, setName] = useState(''); return ( <input value={name} onChange={(e) => setName(e.target.value)} /> ); } ``` The `value` prop pins what the input shows. When you type, the browser fires a change event, `onChange` calls `setName`, React re-renders, and the input receives the new `value`. The rendered text is always exactly what state says — if `onChange` ignored the keystroke, nothing would appear on screen, because the DOM value is overwritten from state on every commit. That round-trip is what makes controlled inputs useful: because the value passes through your code on the way to the screen, you can reject characters, upper-case them, mirror the field into a live preview, disable the submit button while the value is empty, or reset the field by setting state. If you pass `value` but no `onChange` and no `readOnly`, the field becomes unwritable and React logs a warning telling you to add `onChange`, `readOnly`, or use `defaultValue` instead. Candidates often meet controlled inputs for the first time through that warning. ## Uncontrolled: the DOM node is the source of truth ```jsx function NameField() { const inputRef = useRef(null); return <input ref={inputRef} defaultValue="Ada" />; } ``` Here React renders the element once, sets the DOM node's initial value from `defaultValue`, and then stays out of the way. Typing updates the DOM node directly; no state changes and the component does not re-render. When you need the value — usually on submit — you read `inputRef.current.value`, or you build a `FormData` from the surrounding `<form>`. The critical property of `defaultValue` is that it is an *initial* value. Once the input is mounted, passing a different `defaultValue` on a later render does not change what the user sees. If you need the field to pick up a new default (a different record loaded into the same form), you remount it — for example by giving the form a new `key` — or you write the DOM value through a ref. ## The same idea across the other form elements - Checkboxes and radios use `checked` / `defaultChecked` rather than `value`, and their handler reads `e.target.checked`. - `<textarea>` in React takes `value` / `defaultValue` as props instead of putting the text between the tags as plain HTML does. - `<select>` takes `value` / `defaultValue` on the select itself rather than a `selected` attribute on an option; a `multiple` select takes an array. - `<input type="file">` cannot be controlled at all — browsers refuse programmatic assignment of a file selection. ## Choosing between them Reach for **controlled** when the value must be visible to the rest of the render: live validation messages, formatting as you type, a character counter, a submit button that enables on non-empty input, one field constraining another, or a value that some other part of the app can also set. Reach for **uncontrolled** when the value is only needed once, at submit. It is less wiring per field, it plays well with browser autofill and native form semantics, and it does not re-render on every keystroke. It is also the pragmatic choice when a non-React widget or a browser feature owns the field. The re-render argument is worth stating honestly in an interview: a controlled input re-renders the component that owns the state on each keystroke, and React then updates one DOM property — that is cheap in a normal form. It only becomes a real cost when the state sits high up and re-renders a large subtree, which is a structural problem rather than a reason to distrust controlled inputs. Measure before restructuring. ## The one-line summary an interviewer wants "Controlled means `value` plus `onChange`, with React state as the single source of truth; uncontrolled means `defaultValue` for the initial render and the DOM keeps the value until I read it." Everything else — the warning about switching between the two, reading fields with refs or `FormData`, the file-input exception — hangs off that sentence.
- What happens if you pass a different defaultValue to an input that is already mounted?Nothing visible. `defaultValue` only sets the DOM node's value when the input first mounts; later renders ignore it. To load a different record into the same form, remount the field with a new `key`, or write the new value onto the node through a ref. If you need the field to track changing data continuously, make it controlled instead.
- How does the controlled/uncontrolled split look for a checkbox?Controlled means `checked={agreed}` plus an `onChange` that calls `setAgreed(e.target.checked)`. Uncontrolled means `defaultChecked` and reading `ref.current.checked` later. The common slip is reading `e.target.value` in the handler — for a checkbox that returns the `value` attribute (default `"on"`), not the checked state.
- Is a re-render on every keystroke a reason to avoid controlled inputs?Usually no. React re-renders the component that owns the state and updates one DOM property; for an ordinary form that is well under a frame. It matters when the state sits far above a large subtree, and the fix there is moving state closer to the field rather than abandoning controlled inputs. Profile before assuming.
A controlled input is a display driven by a thermostat set-point your code owns: the screen only ever shows what you set. An uncontrolled input is a sticky note the DOM keeps for you — you write an initial line on it and read it back when you need it.
saying these in an interview costs you the question
- Says uncontrolled inputs are deprecated or always bad practice
- Thinks changing defaultValue later updates a mounted input
- Believes defaultValue is a fallback shown when value is empty
- Passes value with no onChange and expects typing to work
- Claims React needs state to be able to submit a form