React Forms

React forms usually use controlled components: the value of each input is stored in state, and every keystroke updates that state. React becomes the single source of truth for the form data, which makes validation and submission straightforward.

A Controlled Input

value ties the input to state, and onChange copies what the user types back into state. If you set value without onChange, the input becomes read-only.

import { useState } from 'react';

function NameForm() {
  const [name, setName] = useState('');
  return (
    <>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>Hello, {name || 'stranger'}!</p>
    </>
  );
}
(type "Ali")
Hello, Ali!

Handling Multiple Inputs

For bigger forms, keep one object in state and use the input's name attribute to know which field changed.

function ContactForm() {
  const [form, setForm] = useState({ name: '', email: '', topic: 'web', subscribe: false });

  function handleChange(e) {
    const { name, value, type, checked } = e.target;
    setForm({ ...form, [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" type="email" value={form.email} onChange={handleChange} />
      <select name="topic" value={form.topic} onChange={handleChange}>
        <option value="web">Web app</option>
        <option value="mobile">Mobile app</option>
      </select>
      <label>
        <input name="subscribe" type="checkbox" checked={form.subscribe} onChange={handleChange} />
        Subscribe
      </label>
      <button>Send</button>
    </form>
  );
}
{ name: 'Ali', email: 'ali@mail.com', topic: 'mobile', subscribe: true }

The computed property [name] sets whichever field matches the input name. Checkboxes use checked instead of value, so the handler checks the input type.

Simple Form Validation

Store errors in state and compute them on submit (or as the user types). Show messages next to the fields.

const [error, setError] = useState('');

function handleSubmit(e) {
  e.preventDefault();
  if (!form.email.includes('@')) {
    setError('Please enter a valid email.');
    return;
  }
  setError('');
  // send data...
}

{error && <p className="error">{error}</p>}
Please enter a valid email.

Textarea and Select in React

  • In React, <textarea> uses a value prop instead of child text.
  • <select> uses value on the select tag, not selected on an option.
  • For multiple selection, pass multiple={true} and an array as value.
  • File inputs are always uncontrolled; read files from e.target.files.

Uncontrolled Inputs and Form Libraries

For simple cases you can skip state and read values on submit with new FormData(e.target). For large forms with complex validation, libraries such as React Hook Form reduce boilerplate and re-renders.

Frequently asked questions

What is a controlled component in React?

A form element whose value comes from React state and is updated through an onChange handler. React, not the DOM, holds the current value.

Why can I not type in my React input?

You set a value prop without an onChange handler, so React keeps resetting it. Add onChange to update state, or use defaultValue for an uncontrolled input.

How do I handle multiple inputs in a React form?

Store all fields in one state object, give each input a name, and use one handler that updates the field with setForm({ ...form, [e.target.name]: e.target.value }).