Uncontrolled Components
Learn how uncontrolled components let the DOM manage form state directly, and when accessing values through refs is the better choice.
Introduction
In the last lesson you built controlled components, where React state is the single source of truth for every keystroke. That pattern is powerful, but it is not the only way to work with form inputs. React also supports uncontrolled components, where the DOM itself keeps track of the current value, and you only reach in and read it when you actually need it.
Uncontrolled components can feel like a step backward if you have just gotten comfortable with state-driven forms, but they exist for good reasons. Some inputs — like file pickers — must be uncontrolled. Others are simply overkill to wire up to state when all you need is the value at submit time. This lesson shows you how uncontrolled components work, how to read their values with useRef, and how to decide between the two approaches.
- What an uncontrolled component is and how it differs from a controlled one.
- How to use useRef to read a DOM input's value directly.
- Why file inputs in React must always be uncontrolled.
- When uncontrolled components are a better fit than controlled ones.
- A side-by-side comparison to help you choose the right approach.
What is an Uncontrolled Component?
An uncontrolled component is a form element — usually an <input>, <textarea>, or <select> — that manages its own value internally, the same way it would in plain HTML. React does not set the value prop and does not re-render on every keystroke. Instead, the DOM node itself holds the current value, exactly like it always has since before React existed.
To get at that value, you attach a ref to the element. Whenever you need the current value — typically when the user submits a form — you read it directly off the DOM node through the ref, rather than from a piece of React state.
DOM Owns the Value
The input keeps track of its own text; React is not notified on every change.
Read On Demand
You pull the value out only when you need it, usually at submit time.
Less Code
No onChange handler or state variable is required for a simple field.
Familiar HTML Behavior
Uncontrolled inputs behave like plain HTML form elements you may already know.
Reading Values with useRef
The useRef Hook creates a mutable object with a .current property that persists across renders. When you pass a ref to an element's ref attribute, React sets ref.current to that actual DOM node once it is mounted. From there, you can read properties like .value the same way you would with plain JavaScript DOM APIs.
import { useRef } from 'react';
function NameField() {
const nameRef = useRef(null);
function handleClick() {
alert(`Current value: ${nameRef.current.value}`);
}
return (
<div>
<input ref={nameRef} type="text" defaultValue="" />
<button onClick={handleClick}>Show Value</button>
</div>
);
}
export default NameField;Current value: AmolUncontrolled inputs use defaultValue (or defaultChecked for checkboxes) to set an initial value. Using value without an onChange handler would make React treat the field as controlled and read-only, which triggers a console warning.
When Uncontrolled Inputs Make Sense
Uncontrolled components are not "wrong" or old-fashioned — they are simply the right tool for specific situations. Reach for them when you do not need to react to every keystroke.
Simple Forms
A short contact form that only needs values on submit does not benefit from state on every field.
File Inputs
File inputs are read-only from JavaScript's perspective and must be uncontrolled — more on this below.
Integrating Non-React Code
Wrapping a third-party jQuery plugin or a legacy widget is easier when React is not fighting it for control of the value.
One-Off Reads
Grabbing a value only when a button is clicked avoids unnecessary re-renders on every character typed.
File Inputs Must Be Uncontrolled
An <input type="file"> is a special case: its value is set by the user through the operating system's file picker, and for security reasons the browser never lets JavaScript set that value programmatically. Because React cannot control the value, file inputs are always uncontrolled — you read the selected file(s) through a ref instead.
import { useRef } from 'react';
function AvatarUploader() {
const fileRef = useRef(null);
function handleUpload() {
const file = fileRef.current.files[0];
if (file) {
console.log(`Selected file: ${file.name}`);
}
}
return (
<div>
<input type="file" ref={fileRef} accept="image/*" />
<button onClick={handleUpload}>Upload</button>
</div>
);
}
export default AvatarUploader;Selected file: profile.pngControlled vs Uncontrolled
Both approaches are valid; the table below summarizes the key differences to help you decide which one fits a given field.
| Aspect | Controlled | Uncontrolled |
|---|---|---|
| Source of truth | React state | The DOM element itself |
| Value access | Read directly from state | Read via ref.current.value |
| Re-renders | On every keystroke | None from typing alone |
| Validation as you type | Easy — check state on each change | Harder — must read the DOM manually |
| File inputs | Not possible | Required |
| Boilerplate | More (state + onChange) | Less (just a ref) |
Example: An Uncontrolled Form
Here is a small sign-up form where every field is uncontrolled. Values are only read once, when the form is submitted.
import { useRef } from 'react';
function SignupForm() {
const emailRef = useRef(null);
const passwordRef = useRef(null);
function handleSubmit(e) {
e.preventDefault();
const formData = {
email: emailRef.current.value,
password: passwordRef.current.value,
};
console.log(formData);
}
return (
<form onSubmit={handleSubmit}>
<input ref={emailRef} type="email" placeholder="Email" defaultValue="" />
<input ref={passwordRef} type="password" placeholder="Password" defaultValue="" />
<button type="submit">Sign Up</button>
</form>
);
}
export default SignupForm;{ email: 'amol@example.com', password: '••••••••' }Common Mistakes
- Setting value instead of defaultValue on an uncontrolled input, which makes React treat it as read-only and log a warning.
- Trying to set an <input type="file"> value with JavaScript — browsers block this entirely for security.
- Reading ref.current.value before the component has mounted, which causes a null reference error.
- Mixing controlled and uncontrolled patterns on the same field, such as passing both value and a ref that also tries to set it.
Best Practices
- Use uncontrolled components for simple, low-interaction forms and file inputs.
- Always initialize uncontrolled fields with defaultValue or defaultChecked, never value.
- Reach for controlled components when you need live validation, conditional rendering, or to disable a submit button based on input.
- Combine both patterns freely in the same form — a file input can be uncontrolled while a text field next to it is controlled.
- Prefer a single ref per field rather than querying the DOM manually with document.getElementById.
Frequently Asked Questions
Are uncontrolled components considered bad practice in React?
No. They are a normal, supported pattern. Controlled components are recommended when you need to react to every change, but uncontrolled components are perfectly fine — and sometimes preferable — for simpler cases.
Can I mix controlled and uncontrolled fields in one form?
Yes. It is common to keep most fields controlled while leaving a file input, which has no choice but to be uncontrolled, alongside them.
Why does React warn about switching between controlled and uncontrolled?
If an input starts with value={undefined} and later receives a real value (or vice versa), React cannot tell which mode it is in, so it warns you to keep the behavior consistent across renders.
Do uncontrolled inputs support validation?
Yes, but you validate at read time — for example on submit — rather than on every keystroke, since React is not being notified of each change.
Is useRef only for uncontrolled components?
No, useRef has many other uses, like storing a mutable value that does not trigger re-renders or holding a reference to a DOM node for focusing or measuring it. Reading uncontrolled input values is just one common use case.
Key Takeaways
- Uncontrolled components let the DOM manage an input's value instead of React state.
- useRef gives you a handle to the DOM node so you can read its .value when needed.
- File inputs must always be uncontrolled because browsers do not allow JavaScript to set their value.
- Uncontrolled components mean less code but make live validation and conditional UI harder.
- Controlled and uncontrolled fields can be mixed within the same form.
Summary
Uncontrolled components hand value-tracking back to the DOM, and you read the current value through a ref whenever you actually need it — typically on submit. This trades some of the fine-grained control of state-driven forms for simplicity, and it is required for inputs like file pickers.
You now know both major approaches to handling form input in React and when each one fits. Next, you will step away from forms and start exploring how to style your components, beginning with a survey of all the major styling approaches available in React.
- You understand what uncontrolled components are and how they differ from controlled ones.
- You can read a DOM input's value using useRef.
- You know why file inputs must always be uncontrolled.
- You are ready to explore component styling.