A controlled component is a form element whose value is controlled by React state. Instead of allowing the browser to manage the input value independently, React stores the value in state and updates it whenever the user changes the input.
A controlled component is a form element whose current value is managed by React state.
import { useState } from "react";
function App() {
const [name, setName] = useState("");
return (
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
);
}
Here, React state controls the value of the input.
In a controlled component, the state contains the current value of the form element.
const [name, setName] = useState("");
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
The value comes from state, while onChange
updates the state.
The following is a simple controlled text input.
import { useState } from "react";
function App() {
const [name, setName] = useState("");
return (
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<p>
Name: {name}
</p>
</div>
);
}
export default App;
The value attribute connects an input to React state.
const [email, setEmail] = useState("");
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
Whenever the state changes, React updates the input value.
The onChange event is used to update state when the user
changes an input.
function handleChange(e) {
setName(e.target.value);
}
This creates a connection between the user's input and React state.
import { useState } from "react";
function App() {
const [username, setUsername] = useState("");
return (
<div>
<label>
Username:
</label>
<input
type="text"
value={username}
onChange={(e) =>
setUsername(e.target.value)
}
/>
<p>
Username: {username}
</p>
</div>
);
}
const [email, setEmail] = useState("");
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter email"
/>
The email field always receives its current value from React state.
const [password, setPassword] = useState("");
<input
type="password"
value={password}
onChange={(e) =>
setPassword(e.target.value)
}
/>
The password input is also controlled by React state.
const [age, setAge] = useState("");
<input
type="number"
value={age}
onChange={(e) =>
setAge(e.target.value)
}
/>
The input value is stored in React state.
A textarea can also be controlled using state.
const [message, setMessage] = useState("");
<textarea
value={message}
onChange={(e) =>
setMessage(e.target.value)
}
/>
The state contains the current textarea value.
A select element can be controlled using the
value attribute.
const [course, setCourse] = useState("");
<select
value={course}
onChange={(e) =>
setCourse(e.target.value)
}
>
<option value="">
Select Course
</option>
<option value="react">
React
</option>
<option value="python">
Python
</option>
</select>
Checkboxes are controlled using the checked attribute.
const [agree, setAgree] = useState(false);
<input
type="checkbox"
checked={agree}
onChange={(e) =>
setAgree(e.target.checked)
}
/>
The state is a boolean value: true or false.
Radio buttons can also be controlled with React state.
const [gender, setGender] = useState("");
<input
type="radio"
name="gender"
value="male"
checked={gender === "male"}
onChange={(e) =>
setGender(e.target.value)
}
/>
<input
type="radio"
name="gender"
value="female"
checked={gender === "female"}
onChange={(e) =>
setGender(e.target.value)
}
/>
A form can have multiple controlled inputs.
const [name, setName] = useState("");
const [email, setEmail] = useState("");
<input
value={name}
onChange={(e) =>
setName(e.target.value)
}
/>
<input
value={email}
onChange={(e) =>
setEmail(e.target.value)
}
/>
For forms with several fields, the values can be stored in one object.
const [form, setForm] = useState({
name: "",
email: "",
phone: ""
});
This keeps related form values together.
The spread operator can preserve the existing fields while updating one field.
function handleChange(e) {
setForm({
...form,
[e.target.name]: e.target.value
});
}
The name attribute identifies which field is being
updated.
<input
name="name"
value={form.name}
onChange={handleChange}
/>
<input
name="email"
value={form.email}
onChange={handleChange}
/>
One change handler can update multiple fields when each field has a
matching name property.
function handleChange(e) {
const { name, value } = e.target;
setForm({
...form,
[name]: value
});
}
A controlled form can be submitted using the values stored in React state.
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
<form onSubmit={handleSubmit}>
{/* controlled fields */}
<button type="submit">
Submit
</button>
</form>
Because React has the current form values in state, validation can easily be performed before submission.
function handleSubmit(e) {
e.preventDefault();
if (form.name === "") {
alert("Name is required");
return;
}
console.log("Form submitted");
}
const [error, setError] = useState("");
function handleSubmit(e) {
e.preventDefault();
if (form.name === "") {
setError("Please enter your name");
return;
}
setError("");
}
The error message can then be displayed in the JSX.
{error && (
<p>
{error}
</p>
)}
A placeholder can provide a hint to the user while the input is empty.
const [city, setCity] = useState("");
<input
type="text"
value={city}
onChange={(e) =>
setCity(e.target.value)
}
placeholder="Enter your city"
/>
To clear a controlled form, reset the state values.
function resetForm() {
setForm({
name: "",
email: "",
phone: ""
});
}
Because the inputs receive their values from state, they are cleared automatically after the state changes.
import { useState } from "react";
function App() {
const [name, setName] = useState("");
function showName() {
alert(name);
}
return (
<div>
<input
value={name}
onChange={(e) =>
setName(e.target.value)
}
/>
<button onClick={showName}>
Show Name
</button>
</div>
);
}
export default App;
A controlled input gets its value from React state.
const [name, setName] = useState("");
<input
value={name}
onChange={(e) =>
setName(e.target.value)
}
/>
React knows the current value of the input and can use it for validation, display, submission, and other application logic.
Controlled components make it easy to react to user input and update other parts of the interface.
const [name, setName] = useState("");
return (
<div>
<input
value={name}
onChange={(e) =>
setName(e.target.value)
}
/>
<h2>
Welcome, {name}
</h2>
</div>
);
import { useState } from "react";
function App() {
const [form, setForm] = useState({
email: "",
password: ""
});
function handleChange(e) {
setForm({
...form,
[e.target.name]: e.target.value
});
}
function handleSubmit(e) {
e.preventDefault();
console.log(form);
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
placeholder="Email"
/>
<br />
<input
type="password"
name="password"
value={form.password}
onChange={handleChange}
placeholder="Password"
/>
<br />
<button type="submit">
Login
</button>
</form>
);
}
export default App;
A common mistake is using a value without providing a way
to update the state.
For example:
const [name, setName] = useState("");
<input
value={name}
/>
The input is controlled by name, but there is no
onChange handler to update it.
A controlled input normally needs both a value and a way to update that value.
<input
value={name}
onChange={(e) =>
setName(e.target.value)
}
/>
The basic workflow of a controlled component is:
onChange event runs.User Input
↓
onChange
↓
setState()
↓
React State
↓
Re-render
↓
Updated Input
value for controlled text inputs.checked for controlled checkboxes.onChange to update form state.name attributes.onSubmit for form submission.value attribute connects an input to state.onChange event updates the state.checked.value.Question: What makes a form element a controlled component in React?