Forms in React

Forms are one of the most important parts of any web application. They allow users to enter information — signing up, logging in, searching, filling out contact details, and more. React provides a structured way to manage form input using a pattern called controlled components.

In a controlled component, React state is the single source of truth for all form values. The input field's value is always driven by state, and state is always updated through event handlers.

Controlled vs Uncontrolled Components

There are two ways to handle form inputs in React:

  • Controlled components — The input value is stored in React state. React controls what the input shows at all times. This is the recommended approach.
  • Uncontrolled components — The input manages its own value internally, and React reads it only when needed (using useRef). This is less common and harder to manage.

This topic focuses on controlled components, which are the standard in React development.

A Simple Text Input (Controlled)


import { useState } from 'react';

function NameForm() {
  const [name, setName] = useState("");

  function handleChange(event) {
    setName(event.target.value);
  }

  function handleSubmit(event) {
    event.preventDefault();
    alert(`Submitted name: ${name}`);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={name} onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

Here, the input's value is tied to the name state. Every keystroke calls handleChange, which updates the state. The input always displays whatever is in the state.

Handling Multiple Inputs

When a form has multiple fields, each field can have its own state variable, or a single state object can manage all values at once. Using one state object is often cleaner for forms with several fields:


import { useState } from 'react';

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

  function handleChange(event) {
    const { name, value } = event.target;
    setFormData({ ...formData, [name]: value });
  }

  function handleSubmit(event) {
    event.preventDefault();
    console.log("Form submitted:", formData);
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Username:</label>
        <input
          type="text"
          name="username"
          value={formData.username}
          onChange={handleChange}
        />
      </div>

      <div>
        <label>Email:</label>
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
      </div>

      <div>
        <label>Password:</label>
        <input
          type="password"
          name="password"
          value={formData.password}
          onChange={handleChange}
        />
      </div>

      <button type="submit">Register</button>
    </form>
  );
}

The key trick here is using name attributes on each input that match the keys in the state object. When any input changes, handleChange reads the input's name attribute to know which field to update, using computed property syntax: [name]: value.

Handling Textarea

In HTML, a textarea has its value between its tags. In React, it behaves like a text input — with a value prop:


import { useState } from 'react';

function FeedbackForm() {
  const [message, setMessage] = useState("");

  return (
    <div>
      <label>Feedback:</label>
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        rows={4}
      />
      <p>Characters typed: {message.length}</p>
    </div>
  );
}

Handling Select Dropdowns

Select elements work the same as text inputs — controlled via value and onChange:


import { useState } from 'react';

function CountrySelector() {
  const [country, setCountry] = useState("us");

  return (
    <div>
      <label>Country:</label>
      <select value={country} onChange={(e) => setCountry(e.target.value)}>
        <option value="us">United States</option>
        <option value="uk">United Kingdom</option>
        <option value="ca">Canada</option>
        <option value="au">Australia</option>
      </select>
      <p>Selected: {country}</p>
    </div>
  );
}

Handling Checkboxes

Checkboxes use the checked prop instead of value:


import { useState } from 'react';

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

  return (
    <div>
      <label>
        <input
          type="checkbox"
          checked={agreed}
          onChange={(e) => setAgreed(e.target.checked)}
        />
        I agree to the terms and conditions
      </label>
      {agreed && <p>Thank you for agreeing!</p>}
    </div>
  );
}

Note the use of e.target.checked (not e.target.value) for checkboxes, since a checkbox's state is a boolean — either checked or not.

Basic Form Validation

Validation can be added before processing the form submission:


import { useState } from 'react';

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

  function handleSubmit(event) {
    event.preventDefault();

    if (!email.includes("@")) {
      setError("Please enter a valid email address.");
      return;
    }

    setError("");
    alert(`Logging in with: ${email}`);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
      />
      {error && <p>{error}</p>}
      <button type="submit">Log In</button>
    </form>
  );
}

Key Points

  • Controlled components link form input values to React state via the value prop.
  • Every input change calls an onChange handler that updates state.
  • Use event.preventDefault() in form submit handlers to stop page reloads.
  • Multiple form fields can be managed with a single state object using the input's name attribute.
  • Checkboxes use checked and e.target.checked, while other inputs use value and e.target.value.
  • Basic validation can be added inside the submit handler before processing data.

The next topic covers the useEffect Hook — one of the most important Hooks in React, used to handle side effects like fetching data, timers, and subscriptions.

Leave a Comment

Your email address will not be published. Required fields are marked *