React Controlled Components: Inputs, Checkboxes, and Forms
Build React controlled inputs with value, checked, and onChange. Includes a complete form, reset behavior, number fields, and fixes for uncontrolled-input warnings.
Last reviewed: November 2026
🔍 What Is It?
A controlled component keeps its value in React state:
const [email, setEmail] = useState('');
<input value={email} onChange={e => setEmail(e.target.value)} />Contrast with an uncontrolled component where the DOM owns the value:
const inputRef = useRef<HTMLInputElement>(null);
<input ref={inputRef} />
// read when mounted: inputRef.current?.valueBoth approaches are supported. Choose controlled state when rendering or validation depends on the current value; choose an uncontrolled input with defaultValue, a ref, or FormData when you only need its value at submission.
Choose the right controlled prop
| Control | State prop | Change handler reads |
|---|---|---|
| Text input or textarea | value as a string | event.target.value |
| Checkbox | checked as a boolean | event.target.checked |
| Select | value matching an option | event.target.value |
| Number input | value as a string while editing | event.target.value; parse when needed |
Initialize a controlled text value to an empty string, not undefined or null. Keep its ownership stable for that input's lifetime. defaultValue and defaultChecked set initial uncontrolled values; they do not keep an input synchronized with later state changes.
🎯 Why Do We Need It?
Controlled components give you:
- →Real-time validation — validate as the user types
- →Conditional rendering — show/hide fields based on other field values
- →Pre-filling — populate forms from API data
- →Programmatic reset — clear the form after submission
- →Derived state — compute dependent fields automatically
- →Testability — test form behaviour by simulating state changes
🏗️ What Happens in Real Projects?
In a claims submission form:
- →Multi-step form where step 2 data depends on step 1 selections
- →Real-time field validation (email format, phone number, date ranges)
- →Auto-populate fields from a policy lookup
- →Conditional fields (show "secondary claimant" section only if "joint claim" is checked)
- →Calculate premium estimate as the user fills in coverage details
⚙️ How It Works
The controlled input cycle:
- State holds the current value:
const [value, setValue] = useState('') - Input renders with the state value:
<input value={value} /> - User types a character
- onChange fires:
onChange={e => setValue(e.target.value)} - State updates
- React re-renders the input with the new value
Update the state backing value or checked synchronously in onChange. Debounce an expensive search request or derived calculation separately rather than delaying the input's own state update. For an intentionally read-only input, provide readOnly explicitly.
Number fields and resetting
Keep a number input's editable state as text so an empty field remains representable. Convert and validate at the boundary where a number is required; Number('') is 0, so test emptiness first. A native form reset does not replace React state: reset the controlled state as part of your reset handler.
💻 Example
Controlled Forms in Production — A complete native-input example followed by an enterprise form excerpt using project UI components.
🤔 Common Confusions
❓ What is the difference between controlled and uncontrolled components?
A controlled input receives value or checked from React state and updates that state in onChange. An uncontrolled input keeps its current value in the DOM and can be read through a ref or FormData. Both are valid; choose based on what the UI needs to observe and update.
❓ Why does my input become read-only?
If you pass a value prop without an onChange handler, React makes the input read-only (it controls the value but nothing updates it). Fix: always pair value with onChange. If you want an initial value that the user can then freely edit, use defaultValue instead of value.
🚨 Common Production Issues
**Typing lag** — A state update can re-render an expensive surrounding tree. Keep input state near the form, profile slow work, and defer expensive dependent UI when appropriate. A form library can help, but changing libraries is not the first diagnostic step.
**Bug: Forgetting onChange** — A value prop without onChange makes the input read-only. React will warn in development but it is easy to miss.
**Mutation or stale state** — Do not assign form.field directly. When the new object depends on the previous value, use a functional update such as setForm(previous => ({ ...previous, field: value })).
**Uncontrolled-to-controlled warning** — An initial undefined value later becomes a string, or checked changes from undefined to a boolean. Initialize deliberately or normalize missing API values at the boundary.
✅ Best Practices
Use a single state object for related form fields
Clear field errors on change for better UX
Validate on submit, not on every change (unless real-time feedback is needed)
Retain browser validation where it meets the form requirements; use noValidate only when deliberately providing equivalent custom feedback.
Use checked for checkboxes and value for text; keep the state type stable.
When API data arrives, normalize missing values and avoid overwriting edits on every background refetch.
🏛️ Architect's Note
Choose form ownership based on behavior, not a fixed field-count cutoff. Controlled state is useful when other UI depends on current edits. Uncontrolled inputs can simplify submit-only collection. Libraries add field registration, validation, and subscription models, but still need deliberate defaults, reset behavior, accessibility, and a policy for incoming server data.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
↗ Where this topic fits
React Technology Guide
Return to the React learning hub.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
Sources
Sources / References
Use these React references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide React learning path
Browse the full React topic collection and learning order on SplashRide.
React Interview Prep
Practice React interview questions and scenario-based answers.
Frontend Engineer Career Path
A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.
Python Developer Career Path
A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.
React Hook Form
Master React Hook Form — the industry-standard library for forms in React. Learn registration, validation with Zod, error handling, and the patterns used in enterprise applications.
State
Understand React state — the internal memory of a component that drives UI updates. Learn when to use local state, how to structure it, and the patterns senior engineers use to avoid the most common state bugs.