Ch. 3 · React

Controlled vs Uncontrolled Inputs in React

Compare controlled and uncontrolled React inputs with examples, reset behavior, checkbox handling and fixes for the uncontrolled-to-controlled warning.

~3 min readbeginner

The difference between a controlled and an uncontrolled React input is who owns the current value. A controlled input receives that value from React and reports edits through a handler. An uncontrolled input lets the browser keep the current value, which you read when needed.

Neither is universally better. Choose based on whether the rest of the interface needs to react to every edit, how the form resets and how you collect its data.

A controlled text input

import { useState } from 'react';

export function DisplayName() {
  const [name, setName] = useState('');

  return (
    <label>
      Display name
      <input
        name="displayName"
        value={name}
        onChange={(event) => setName(event.target.value)}
      />
      <small>{name.length} characters</small>
    </label>
  );
}
JSX

The value prop is authoritative. The handler updates state, and the next render supplies the new value. The character count naturally follows the same state. If you provide value without a working change handler or an intentional read-only contract, typing will not update the value as users expect.

Keep the update that controls typing immediate. Debounce a search request or an expensive dependent calculation separately, rather than delaying the input’s own state update.

An uncontrolled form

export function SearchForm({ initialQuery = '', onSearch }) {
  function submit(event) {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    onSearch(String(data.get('query') ?? '').trim());
  }

  return (
    <form onSubmit={submit}>
      <label>
        Search
        <input name="query" defaultValue={initialQuery} />
      </label>
      <button type="submit">Search</button>
      <button type="reset">Reset</button>
    </form>
  );
}
JSX

defaultValue provides the initial value. It does not instruct React to keep overwriting the user’s edits as the prop changes. The name attribute defines the field’s key in FormData; the label supplies its accessible name. These are separate responsibilities.

This pattern suits a form whose values are needed on submission and whose UI does not need React state for each keystroke. A form library may combine DOM ownership with subscriptions, so inspect its contract rather than assuming every library uses one universal model.

Fix the uncontrolled-to-controlled warning

A common source of the warning is initializing a controlled input’s value with undefined, then supplying a string after a request finishes. Keep the value’s ownership consistent across renders.

// Consistently controlled, even before the record arrives:
<input value={draftName ?? ''} onChange={handleNameChange} />
JSX

The fallback prevents an ownership switch, but it does not solve draft synchronization. If new server data arrives while someone is editing, decide whether to preserve the draft, merge untouched fields or offer an explicit reset. Blindly copying refreshed props into state can erase work.

Checkboxes, numbers and files

For a controlled checkbox, use checked and read event.target.checked. Its value is not its boolean checked state.

<input
  type="checkbox"
  checked={subscribed}
  onChange={(event) => setSubscribed(event.target.checked)}
/>
JSX

A numeric field can reasonably keep a string draft because an empty field or a partially typed value may not be a valid number yet. Parse and validate at a deliberate boundary; avoid coercing every intermediate edit into a number and unexpectedly changing the user’s text.

File inputs are selected through the browser’s file picker. Read their selected files rather than trying to assign a local filesystem path as a controlled value. Client file checks improve feedback, but the server must validate uploads independently.

Reset and performance trade-offs

For controlled forms, reset the state that supplies the values. For uncontrolled fields, a native reset returns them to their defaults. Changing a component’s key is another way to reset its subtree, but use meaningful record identity rather than a random key on every render.

A controlled form rerendering on edits is not automatically a performance problem. Keep rapidly changing draft state near the form, profile expensive descendants and avoid broadcasting every keystroke through an unrelated global provider. Uncontrolled ownership can reduce some React updates, but it also changes how live validation and dependent UI obtain values.

Interview answer and practice

“A controlled input reads its current value from React state; an uncontrolled input keeps that value in the DOM. I use controlled state when other UI needs to react to edits and an uncontrolled approach when submit-time collection is sufficient. I keep ownership consistent, define reset behavior and validate on the server regardless of the client approach.”

Practice with React forms questions, compare state and identity MCQs, and review React’s input reference.

More in React

read ✓React · mid

useEffect Pitfalls Interviewers Love to Ask

Stale closures, missing cleanup, fetch races, Strict Mode's double run, infinite loops and effects you don't need, plus how useLayoutEffect differs in timing.

~7 min readread →
read ✓React · mid

React Reconciliation & Keys: What Really Happens

Render vs commit, the diffing rules React relies on, why index keys break inputs, how a key resets state, and what Fiber and batching change about updates.

~7 min readread →
esc