Controlled vs Uncontrolled
Choose between React-driven state and letting the DOM hold the value.
Syntax
// controlled: value + onChange
// uncontrolled: defaultValue + refThere are two ways to handle form inputs in React.
Controlled
React state is the source of truth. The input's value comes from state and updates on onChange. Best when you need instant validation, formatting, or to react to every keystroke.
Uncontrolled
The DOM keeps the value. You read it when needed via a ref and set an initial value with defaultValue. Simpler for basic forms and file inputs.
Example
import { useRef } from 'react';
function UncontrolledForm() {
const nameRef = useRef(null);
function handleSubmit(e) {
e.preventDefault();
console.log(nameRef.current.value);
}
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} defaultValue="Ada" />
<button>Submit</button>
</form>
);
}When to use it
- A live username-availability checker uses a controlled input so the component can read the current value synchronously on every keystroke to debounce API calls.
- A file-upload widget uses an uncontrolled input with a ref because file inputs cannot be made fully controlled in React and only need their value read on submit.
- A third-party date picker library manages its own internal state, so the parent treats it as uncontrolled and reads the selected date through a ref on form submit.
More examples
Controlled input
React owns the input's value through state; every keystroke updates state and re-renders the input.
import { useState } from 'react';
function ControlledEmail() {
const [email, setEmail] = useState('');
return (
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
/>
);
}Uncontrolled input with ref
Lets the DOM own the value (defaultValue, not value) and reads it through a ref only on form submission.
import { useRef } from 'react';
function UncontrolledForm() {
const nameRef = useRef(null);
function handleSubmit(e) {
e.preventDefault();
console.log(nameRef.current.value);
}
return (
<form onSubmit={handleSubmit}>
<input ref={nameRef} defaultValue="" />
<button type="submit">Submit</button>
</form>
);
}Uncontrolled file input
File inputs must be uncontrolled; the ref reads the selected file object only when the user submits.
import { useRef } from 'react';
function FileUpload({ onUpload }) {
const fileRef = useRef(null);
function handleSubmit(e) {
e.preventDefault();
const file = fileRef.current.files[0];
if (file) onUpload(file);
}
return (
<form onSubmit={handleSubmit}>
<input type="file" ref={fileRef} accept="image/*" />
<button type="submit">Upload</button>
</form>
);
}
Discussion