Sitemap

Forms in React

3 min readMar 7, 2026

--

Press enter or click to view image in full size

In a standard HTML form, the DOM itself holds the form data. In React, we typically want the React component to be the “single source of truth” for that data.

This leads us to the two main ways of handling forms: Controlled and Uncontrolled components.

Controlled Components

A controlled component is an input whose value is controlled by React state.

Instead of the DOM storing the input value, React state does. Every time the input changes, it triggers an onChange handler that updates the state. The input’s value is then set by the state.

Why use this?

It gives you absolute control. You can validate input as the user types, format strings (like phone numbers), or disable the submit button based on specific criteria.

import React, { useState } from 'react';

function SimpleForm() {
const [name, setName] = useState('');

// event handler - for any input change
const handleChange = (e) => {
setName(e.target.value);
};

// event handler - for form submission
const handleSubmit = (e) => {
e.preventDefault(); //Prevent default browser behavior (page reload)
alert(`Submitted Name: ${name}`);
};

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

Handling Multiple Inputs

Writing a separate useState and a handler for every single input (Email, Password, Age, etc.) can become repetitive. The professional way to handle this is to use a single object in state and a generic handler function.

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

const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData, // Copy existing fields
[name]: value // Update only the field that changed
});
};

// Usage in JSX
<input name="username" value={formData.username} onChange={handleChange} />
<input name="email" value={formData.email} onChange={handleChange} />

Other Form Elements

React handles dropdowns, checkboxes, and textareas slightly differently than HTML to maintain consistency:

  • Textarea: Uses a value attribute instead of inner text (<textarea value={bio} />).
  • Select: The selected attribute is not used on <option>. Instead, you use value on the root <select> tag.
  • Checkbox: Uses the checked attribute instead of value.
import React, { useState } from "react";

function MiniForm() {
const [bio, setBio] = useState("");
const [fruit, setFruit] = useState("apple");
const [subscribe, setSubscribe] = useState(false);

return (
<form>
<textarea value={bio} onChange={e => setBio(e.target.value)} />
<select value={fruit} onChange={e => setFruit(e.target.value)}>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
</select>
<input type="checkbox" checked={subscribe} onChange={e => setSubscribe(e.target.checked)} />
</form>
);
}

Uncontrolled Components

In an uncontrolled component, you don’t sync the input with the state. Instead, you use a Ref to “pull” the value from the DOM only when you need it (like when the user clicks submit).

Get Nimmikrishnab’s stories in your inbox

Join Medium for free to get updates from this writer.

When to use?

It’s useful for non-React code integration or very simple forms where you don’t need real-time validation.

import { useRef } from 'react';

function UncontrolledForm() {
const inputRef = useRef(null);

// form submission handler
const handleSubmit = (e) => {
e.preventDefault();
console.log("Value:", inputRef.current.value);
};

return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
}

Form Libraries: When it Gets Complex

As forms grow to include complex validation (e.g., “password must contain a symbol”), manual state management becomes a headache. Most professional React teams use libraries like:

  • React Hook Form: Lightweight and high-performance.
  • Formik: Comprehensive and widely used for enterprise apps.
  • Zod/Yup: Often paired with the above for schema-based validation.

Summary

  • Controlled components are the standard for most React apps.
  • Every input needs
value={state}
onChange={handleChange}
  • Use e.target.name to handle multiple inputs with a single function.
  • Always call e.preventDefault() on submit to prevent page refreshes.
  • Use checked for checkboxes and value for everything else.
  • Consider React Hook Form if your form has more than 5–10 fields.

--

--