Forms in React: Controlled Components

Learn the fundamental React pattern for creating interactive and predictable forms that are easy to manage and validate.

Lesson ProgressStep 1 of 10
📝
0 EXP

Welcome! Let's learn how to capture user input with forms in React.

/* Let's build a form... */

State: The Single Source of Truth

The foundation of forms in React is state. For each input field, you'll have a piece of state that holds its current value. This makes your React component the "single source of truth" for the form's data.

We use the `useState` hook to create this state. It returns an array with two things: the current value, and a function to update that value.

import { useState } from 'react';

const [username, setUsername] = useState('');

Here, `username` holds the current value (initially an empty string `''`), and `setUsername` is the function we'll use to change it.

System Check

Which React hook is essential for creating a state variable to hold form data?

Advanced Holo-Simulations

0 EXP

Log in to unlock these advanced training modules and test your skills.


Achievements

🏆
State Binder

Correctly bind a form input's value to React state.

🏗️
Event Whiz

Use the onChange handler to update state from an input.

✍️
Submission Expert

Handle a form submission using onSubmit and preventDefault.

Mission: Build a Controlled Component

Complete the `LoginForm` component. Add a state variable for an `email` and connect it to an `<input>` to make it a fully controlled component.

A.D.A. Feedback:

> System integrity looks stable. Code is valid.

Challenge: Order the Logic

Drag these lines of code into the correct logical order as they would appear in a component.

const handleChange = (e) => setName(e.target.value)
const [name, setName] = useState('')
<input value={name} onChange={handleChange} />

Challenge: Complete the Form Submission

Fill in the missing parts to properly handle a form submission.

const= (e) => {
e.;
//... send data
};

return ( <form={handleSubmit}>...</form> );

Consult A.D.A.

Community Holo-Net

Peer Project Review

Submit your "Controlled Component" project for feedback from other Net-Runners.

Beyond the Basics: Advanced React Form Techniques

You've mastered the "controlled component" pattern for a single text input, which is the foundation of form handling in React. This pattern—State + `value` + `onChange`—provides a robust "single source of truth" and gives you complete control.

But real-world forms are rarely just one field. They have multiple inputs, dropdowns, checkboxes, and complex validation. Let's explore how to scale the controlled component pattern to build any form you need.

1. Handling Multiple Inputs

You have two primary approaches for managing a form with many fields:

  • Multiple `useState` Hooks: Simple and clear for a few inputs.
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
  • Single State Object: More scalable for large forms. To update one field, you use the spread (`...`) operator to copy the old state and then overwrite the specific field.
    const [formData, setFormData] = useState({
      email: '',
      password: ''
    });

When using a single state object, you can write a **single generic handler** by giving each input a `name` attribute that matches its key in the state object.

const handleChange = (e) => {
  const { name, value } = e.target;
  setFormData(prevData => ({
    ...prevData,
    [name]: value // [name] is computed property syntax
  }));
}

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

2. Handling Other Form Elements

The controlled pattern adapts slightly for different input types.

Textarea

A `<textarea>` in React is surprisingly simple. Instead of using children (like in HTML), you just use the `value` prop, exactly like an `<input>`.

<textarea value={bio} onChange={(e) => setBio(e.target.value)} />

Select (Dropdown)

Similarly, you don't use the `selected` attribute on `<option>` tags. You control the selected value by setting the `value` prop on the root `<select>` tag.

<select value={userRole} onChange={(e) => setUserRole(e.target.value)}>
  <option value="user">User</option>
  <option value="admin">Admin</option>
</select>

Checkboxes

Checkboxes are different. You don't read `e.target.value`. Instead, you read the boolean `e.target.checked`.

const [agreed, setAgreed] = useState(false);

<input
  type="checkbox"
  checked={agreed} // Use 'checked' prop, not 'value'
  onChange={(e) => setAgreed(e.target.checked)}
/>

3. Client-Side Validation

Since your form's data lives in state, you can validate it at any time. A common pattern is to run validation inside the `onSubmit` handler *before* sending data to a server. You can store error messages in another state object.

const [errors, setErrors] = useState({});

const handleSubmit = (e) => {
  e.preventDefault();
  const newErrors = {};

  if (formData.password.length < 8) {
    newErrors.password = 'Password must be at least 8 characters';
  }
  if (!formData.email.includes('@')) {
    newErrors.email = 'Must be a valid email';
  }

  setErrors(newErrors);

  // Only submit if there are no errors
  if (Object.keys(newErrors).length === 0) {
    console.log('Form is valid!', formData);
    // ...send data to API
  }
}

// In your JSX:
<input name="email" ... />
{errors.email && <p className="error">{errors.email}</p>}

4. The "Uncontrolled" Alternative: `useRef`

For very simple forms or when integrating with non-React code, you can use "uncontrolled components." Here, the DOM stores the value, not React state. You use the `useRef` hook to get a reference to the DOM node and pull its value *only* when you need it (like on submit).

const emailRef = useRef(null);

const handleSubmit = (e) => {
  e.preventDefault();
  // Access the value directly from the DOM node
  console.log(emailRef.current.value);
}

// No value or onChange. Just 'ref'.
<input type="email" ref={emailRef} />

**When to use uncontrolled?** Almost never. Always default to **controlled components**. They are more predictable and fit the React data flow. Uncontrolled is only for rare cases, like managing file inputs (`<input type="file">`).

Key Takeaway: The controlled component pattern scales to all form types. Use a single state object and computed property names (`[name]`) for clean, multi-input forms. Always prefer controlled components over uncontrolled for a predictable, React-centric data flow.

React Forms Glossary

Controlled Component
A form input element (like `<input>`, `<textarea>`, or `<select>`) whose value is controlled by React state. The component's state is the **single source of truth**.
Uncontrolled Component
A form input where the DOM handles its own state (the traditional HTML way). In React, you use a `useRef` to "pull" the value from the DOM when needed, rather than having React "push" the value to it.
`useState`
The React hook used to declare a state variable. For forms, this is where you store the input's current value (e.g., `const [name, setName] = useState('');`).
`onChange`
The event handler prop on a form element that fires every time the user changes its value (e.g., on every keystroke). This is where you call your state setter function.
`onSubmit`
The event handler prop on the `<form>` element. It fires when the form is submitted (e.g., by clicking a `type="submit"` button or pressing Enter).
`event.target.value`
Inside an `onChange` handler, this property contains the new, up-to-date value of the input that triggered the event.
`event.target.checked`
For inputs of `type="checkbox"`, this boolean property (true/false) is used instead of `event.target.value` to get the element's state.
`event.preventDefault()`
A method called inside the `onSubmit` handler. It stops the browser's default behavior of submitting the form, which would cause a full-page refresh.
Single Source of Truth
A core principle in React. It means that for any piece of data (like a form input's value), only one component "owns" that data by holding it in its state. The UI is a direct reflection of that state.

About the Author

Author's Avatar

TodoTutorial Team

Passionate developers and educators making programming accessible to everyone.

This article was written and reviewed by our team of web development experts, who have years of experience teaching React and building robust, scalable web applications.

Verification and Updates

Last reviewed: October 2025.

We strive to keep our content accurate and up-to-date. This tutorial is based on the latest React specifications (v18+) and is periodically reviewed to reflect industry best practices.

External Resources

Found an error or have a suggestion? Contact us!