Controlled Components (Forms)
โš›๏ธ React 18+ ๐ŸŸข Chapter 14 of 39 ๐Ÿ“‚ Phase 06: Forms ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: Controlled Input ยท Text/Number/Checkbox/Radio/Select/Textarea ยท Multiple Inputs ยท Form Submit ยท Form Reset
A "controlled component" is a form input whose value is driven entirely by React state, rather than managed internally by the browser's own DOM โ€” this gives you full control over validation, formatting, and submission.
1Building a Controlled Text Input
๐Ÿ’ป Example 1: A Controlled Login Form
import { useState } from "react";

function LoginForm() {
  const [email, setEmail] = useState("");

  function handleSubmit(event) {
    event.preventDefault();
    console.log(email);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(event) => setEmail(event.target.value)}
      />
      <button type="submit">Login</button>
    </form>
  );
}
๐Ÿ” Why 'Controlled'?

The input's value is always exactly whatever is in state โ€” the user can't type anything React doesn't know about, since every keystroke fires onChange, which updates state, which re-renders the input with the new value.

2Checkboxes, Radio Buttons, and Select Dropdowns
const [agreed, setAgreed] = useState(false);
const [plan, setPlan] = useState("basic");

<input
  type="checkbox"
  checked={agreed}
  onChange={(e) => setAgreed(e.target.checked)}
/>

<select value={plan} onChange={(e) => setPlan(e.target.value)}>
  <option value="basic">Basic</option>
  <option value="pro">Pro</option>
</select>

Checkboxes use checked (not value) paired with event.target.checked. Selects work like text inputs but with event.target.value matching one of the <option> values.

3Managing Multiple Inputs with One State Object
const [form, setForm] = useState({ name: "", email: "" });

function handleChange(event) {
  const { name, value } = event.target;
  setForm((prev) => ({ ...prev, [name]: value }));
}

<input name="name" value={form.name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />

Using one handleChange function for every field, keyed off each input's name attribute, avoids writing a separate handler per field โ€” a standard pattern once a form grows past two or three inputs.

โš ๏ธ Setting value Without an onChange Handler

An input with value={email} but no onChange becomes permanently read-only โ€” React logs a console warning, and the user literally cannot type anything, since state never updates in response to their keystrokes. Every controlled input needs both value and onChange together.

๐Ÿ’ป Hands-on Interactive Practice Challenge

Build a controlled form with name and email text inputs, plus a checkbox for "Subscribe to newsletter," logging all the values on submit.

React Practice Challenge โ–ถ Run in Compiler
import { useState } from "react";

function SignupForm() {
  const [form, setForm] = useState({ name: "", email: "", subscribe: false });

  function handleChange(e) {
    const { name, value, type, checked } = e.target;
    setForm(prev => ({ ...prev, [name]: type === "checkbox" ? checked : value }));
  }

  function handleSubmit(e) {
    e.preventDefault();
    console.log(form);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={form.name} onChange={handleChange} />
      <input name="email" value={form.email} onChange={handleChange} />
      <input type="checkbox" name="subscribe" checked={form.subscribe} onChange={handleChange} />
      <button type="submit">Sign Up</button>
    </form>
  );
}
Run This Challenge in Online JS IDE โ†’
โ“ Frequently Asked Questions (FAQ)

Q What's the difference between a controlled and uncontrolled input?

A controlled input's value lives in React state and updates through onChange. An uncontrolled input manages its own value internally in the DOM, and you'd read it only when needed using a ref (covered in Chapter 20: useRef).

Q Why use event.target.name to handle multiple inputs?

It lets one handleChange function update the correct field in a single state object dynamically, using [name]: value computed-property syntax, instead of writing a near-identical handler for every single input on the form.

OC
Written by Our Compiler Technical Editorial Team
Reviewed for accuracy & tested on React 18+ ยท Last updated August 2026