How to Handle Form Input in React

Use controlled inputs in React when form fields should stay fully synced with component state. This makes validation, formatting, submit logic, and conditional UI much easier to manage.

What you’ll build or solve

You’ll learn how to handle form input in React with useState(), shared change handlers, and field-specific updates. You’ll also know how to scale the same pattern across multiple input types.

When this approach works best

This approach is the right choice when the UI needs predictable, real-time access to field values.

Common real-world scenarios include:

  • Login fields
  • Search inputs
  • Checkout forms
  • Profile editors
  • Live validation

This is a bad idea when the form is tiny and simple uncontrolled inputs are intentionally preferred.

Prerequisites

You only need:

  • A React component
  • Basic useState() knowledge
  • Basic JSX event knowledge

Step-by-step instructions

Step 1: Sync the input value with state

The core controlled-input pattern is value plus onChange.

JavaScript

import { useState } from "react";

function SearchInput() {
  const [query, setQuery] = useState("");

  return (
    <input
      type="text"
      value={query}
      onChange={(event) => setQuery(event.target.value)}
      placeholder="Search lessons"
    />
  );
}

export default SearchInput;

This keeps React as the source of truth.

Step 2: Use one handler for multiple fields

For forms, one shared handler scales better.

JavaScript

const [formData, setFormData] = useState({
  email: "",
  password: ""
});

Create a reusable update function.

JavaScript

function handleChange(event) {
  const { name, value } = event.target;

  setFormData((prev) => ({
    ...prev,
    [name]: value
  }));
}

Connect fields by name.

JavaScript

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

This removes repeated state handlers.

Step 3: Handle checkboxes and other input types

Not every field uses value.

Checkboxes use checked.

JavaScript

const [isSubscribed, setIsSubscribed] = useState(false);

<input
  type="checkbox"
  checked={isSubscribed}
  onChange={(event) => setIsSubscribed(event.target.checked)}
/>

This keeps boolean inputs predictable.

What to look for:

  • Text fields use value
  • Checkboxes use checked
  • Shared handlers scale well
  • name keys should match state
  • Controlled inputs simplify validation

Examples you can copy

Single search field

JavaScript

value={query}
onChange={(event) => setQuery(event.target.value)}

Shared multi-field handler

JavaScript

[event.target.name]: event.target.value

Checkbox

JavaScript

checked={isSubscribed}

Common mistakes and how to fix them

Mistake 1: Missing value

What the reader might do:

Use onChange without binding the field value.

Why it breaks: the input becomes uncontrolled.

Corrected approach:

Always pair value with onChange.

Mistake 2: Wrong name mapping

What the reader might do:

JavaScript

name="userEmail"

but state uses email.

Why it breaks: the wrong key updates.

Corrected approach:

Align name with the state property.

Mistake 3: Using value for checkboxes

What the reader might do:

JavaScript

value={isSubscribed}

Why it breaks: checkbox state should use checked.

Corrected approach:

Use checked.

Troubleshooting

If typing does not work, confirm the input has both value and onChange.

If shared updates fail, verify name matches the state key.

If checkbox state behaves oddly, switch from value to checked.

If performance drops in huge forms, consider a form library.

Quick recap

  • Use controlled inputs with state
  • Pair value with onChange
  • Use shared handlers for multiple fields
  • Use checked for checkboxes
  • Keep name aligned with state keys