Controlled vs. Uncontrolled Components in React
Exploring How Controlled and Uncontrolled Components Work in React

In React, the terms controlled and uncontrolled primarily describe how form elements (like <input>, <select>, or <textarea>) handle their internal state.
1. Controlled Components
In a controlled component, the form data is handled by the React component state. React is the "single source of truth".
React state controls the input value. Every keystroke updates state.
How it works: The input's value is bound to a state variable (e.g., via useState). Every time the user types, an onChange handler updates that state, which then re-renders the input with the new value.
Best for:
Real-time validation: Checking if a password is long enough as the user types.
Conditional UI: Disabling a submit button until all fields are valid.
Format enforcement: Automatically formatting a phone number or forcing uppercase.
Downside: Can be verbose (requires more boilerplate code) and may cause performance issues in extremely large forms due to frequent re-renders (Since state will update when change in input).
When to use: When you need instant validation, formatting, or conditional rendering based on input.
Example
import { useState } from 'react';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
// Validate before sending
if (!email.includes('@')) {
alert('Invalid email');
return;
}
console.log({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}
2. Uncontrolled Components
In an uncontrolled component, the form data is handled by the DOM itself.
How it works: You use a ref (via useRef) / JavaScript methods to "pull" the value from the DOM element only when you need it (e.g., upon form submission). You can set an initial value using the defaultValue prop.
Best for:
- Simple forms: When you only need the value once at the end.
- Non-React integration: When using third-party libraries that directly manipulate the DOM.
- Large forms: Where reducing re-renders is critical for performance.
One benefit of uncontrolled components is they work seamlessly with browser's built-in features like autoComplete, form validation, and password managers without additional code.
<input
ref={emailRef}
name="email"
autoComplete="email"
// ↑ Browser handles everything automatically
/>
Downside: You lose granular control; you cannot easily react to changes in real-time or enforce complex formatting.
When to use: File inputs (must be uncontrolled), simple forms, third-party DOM integrations, performance-critical inputs.
Example
import { useRef } from 'react';
function FileUpload() {
const fileRef = useRef(null);
const nameRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
const file = fileRef.current.files[0];
const name = nameRef.current.value;
if (!file) {
alert('Select a file');
return;
}
const formData = new FormData();
formData.append('file', file);
formData.append('name', name);
// Send to API
console.log('Uploading:', { name, file: file.name });
};
return (
<form onSubmit={handleSubmit}>
<input
ref={nameRef}
defaultValue="Untitled"
placeholder="File name"
/>
<input
ref={fileRef}
type="file"
accept="image/*"
/>
<button type="submit">Upload</button>
</form>
);
}
Quick Decision Guide
Need live validation/formatting? → Controlled
Just need the value on submit? → Uncontrolled
File input? → Must be uncontrolled
Thousands of inputs? → Consider uncontrolled for performance






