Forms are an essential part of almost every web application.
Users use forms to:
- Create accounts
- Log in
- Search for content
- Submit feedback
- Update profiles
- Place orders
- Upload information
- Contact businesses
- Filter products
- Enter payment details
In traditional HTML, forms are usually handled directly by the browser. React gives you more control by allowing form values, validation, submission, and UI feedback to be connected to component state.
React forms are especially important because they introduce several concepts that appear repeatedly in real-world applications:
- State management
- Event handling
- Controlled components
- Uncontrolled components
- Validation
- Error handling
- Form submission
- Dynamic fields
By the end of this chapter, you will understand how to build forms ranging from a simple input field to a dynamic form with multiple fields and validation.
React Forms Introduction
A form in React is still built using normal HTML elements such as:
<form>
<input />
<textarea />
<select>
<option>Option 1</option>
</select>
<button type="submit">Submit</button>
</form>
The difference is that React can manage the values and behavior of these elements.
For example:
import { useState } from "react";
function LoginForm() {
const [email, setEmail] = useState("");
return (
<form>
<input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
/>
</form>
);
}
Here, React state stores the current value of the input.
When the user types:
hello@example.com
the state is updated to contain the same value.
This creates a connection between the form element and React state.
Form Elements
React forms use standard HTML form elements.
The most common elements are:
| Element | Common Purpose |
|---|---|
<input> | Text, email, password, checkbox, radio, number, etc. |
<textarea> | Multi-line text |
<select> | Selecting an option |
<option> | An option inside <select> |
<button> | Submit or perform an action |
<form> | Groups form controls and handles submission |
For example:
<form>
<input type="text" />
<textarea />
<select>
<option value="react">React</option>
<option value="javascript">JavaScript</option>
</select>
<button type="submit">
Submit
</button>
</form>
React does not replace HTML forms. It gives you JavaScript and state-based control over them.
Input Fields
The <input> element is one of the most frequently used form elements.
A basic input looks like this:
<input type="text" />
You can specify different input types:
<input type="text" />
<input type="email" />
<input type="password" />
<input type="number" />
<input type="date" />
<input type="search" />
Each type provides behavior appropriate to the kind of information being collected.
Handling an Input
You can listen for changes using onChange:
function App() {
function handleChange(event) {
console.log(event.target.value);
}
return (
<input
type="text"
onChange={handleChange}
/>
);
}
event.target.value contains the current value of the input.
Connecting an Input to State
A common React pattern is:
import { useState } from "react";
function App() {
const [name, setName] = useState("");
return (
<input
type="text"
value={name}
onChange={(event) => setName(event.target.value)}
/>
);
}
Now the state and input are connected.
The value comes from:
value={name}
and changes are sent back through:
onChange={(event) => setName(event.target.value)}
This is the foundation of a controlled input.
Placeholder
You can provide a hint using placeholder:
<input
type="email"
placeholder="Enter your email"
/>
A placeholder should provide an example or hint rather than act as the only label for an important field.
Labels
Use labels to make forms easier to understand and more accessible.
<label htmlFor="email">
Email Address
</label>
<input
id="email"
type="email"
/>
The htmlFor value matches the input’s id.
Textarea
The <textarea> element is used for multi-line text.
For example:
<textarea
placeholder="Write your message"
/>
In React, a controlled textarea can be created like this:
import { useState } from "react";
function FeedbackForm() {
const [message, setMessage] = useState("");
return (
<textarea
value={message}
onChange={(event) => setMessage(event.target.value)}
placeholder="Write your feedback"
/>
);
}
The value is stored in React state.
Textarea and HTML
In HTML, you may see:
<textarea>
Default text
</textarea>
In React, when controlling the value, use the value prop:
<textarea value={message} />
For an initial uncontrolled value, React provides defaultValue:
<textarea defaultValue="Initial message" />
This distinction becomes important when learning controlled and uncontrolled components.
Select
The <select> element lets users choose from a list of options.
For example:
<select>
<option value="react">React</option>
<option value="vue">Vue</option>
<option value="angular">Angular</option>
</select>
Controlled Select
You can connect a select element to state:
import { useState } from "react";
function CourseForm() {
const [course, setCourse] = useState("react");
return (
<select
value={course}
onChange={(event) => setCourse(event.target.value)}
>
<option value="react">React</option>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
</select>
);
}
The selected option is stored in course.
Multiple Select
A select element can also allow multiple choices.
<select multiple>
<option value="react">React</option>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
</select>
When controlling a multiple select, the value should be an array.
<select
multiple
value={skills}
onChange={handleChange}
>
You then need to collect the selected options from the event.
Checkbox
Checkboxes are useful when users can independently enable or disable an option.
For example:
<input type="checkbox" />
<label>Subscribe to newsletter</label>
Checkboxes use the checked property rather than value to represent whether they are selected.
A controlled checkbox looks like this:
import { useState } from "react";
function NewsletterForm() {
const [subscribed, setSubscribed] = useState(false);
return (
<label>
<input
type="checkbox"
checked={subscribed}
onChange={(event) =>
setSubscribed(event.target.checked)
}
/>
Subscribe to newsletter
</label>
);
}
The important property is:
event.target.checked
It returns either:
true
or:
false
Checkbox with a Label
A more accessible pattern is:
<label htmlFor="terms">
<input
id="terms"
type="checkbox"
/>
I agree to the terms
</label>
The label makes it easier for users to understand what the checkbox represents.
Radio Buttons
Radio buttons are useful when users should select one option from a group.
For example:
<label>
<input
type="radio"
name="plan"
value="free"
/>
Free
</label>
<label>
<input
type="radio"
name="plan"
value="pro"
/>
Pro
</label>
The same name groups the radio buttons together.
Controlled Radio Buttons
import { useState } from "react";
function PlanForm() {
const [plan, setPlan] = useState("free");
return (
<>
<label>
<input
type="radio"
name="plan"
value="free"
checked={plan === "free"}
onChange={(event) => setPlan(event.target.value)}
/>
Free
</label>
<label>
<input
type="radio"
name="plan"
value="pro"
checked={plan === "pro"}
onChange={(event) => setPlan(event.target.value)}
/>
Pro
</label>
</>
);
}
Here, only one option can be selected.
The state contains:
plan
and might contain either:
"free"
or:
"pro"
Multiple Inputs
Real forms usually contain several inputs.
For example:
function RegistrationForm() {
return (
<form>
<input type="text" />
<input type="email" />
<input type="password" />
<button type="submit">
Create Account
</button>
</form>
);
}
You could create separate state variables:
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
This is perfectly valid.
However, when several fields belong to the same form, an object can sometimes provide a convenient structure.
const [formData, setFormData] = useState({
name: "",
email: "",
password: ""
});
Handling Multiple Inputs with One Handler
You can give each input a matching name:
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
<input
name="password"
value={formData.password}
onChange={handleChange}
/>
Then:
function handleChange(event) {
const { name, value } = event.target;
setFormData((currentData) => ({
...currentData,
[name]: value
}));
}
The computed property:
[name]: value
updates the correct field.
For example, if:
name === "email"
React effectively updates:
email: value
without replacing the other fields.
Image Placement: Multiple Form Inputs
Place this image immediately after the multiple inputs section.
Create a modern 16:9 educational infographic showing a React registration form with Name, Email, Password, Country, and checkbox fields. Visually connect the fields to a central React state object containing the same field names. Show a simple flow from user input to onChange to state. Use a modern Web4Learners developer aesthetic, clean typography, rounded cards, subtle gradients, polished UI, and minimal text.
Form Submission
HTML forms normally submit when the user presses a submit button.
In React, you can handle submission with onSubmit.
function LoginForm() {
function handleSubmit(event) {
event.preventDefault();
console.log("Form submitted");
}
return (
<form onSubmit={handleSubmit}>
<input type="email" />
<input type="password" />
<button type="submit">
Login
</button>
</form>
);
}
Why Use preventDefault()?
By default, the browser may submit the form using its normal HTML behavior, which can cause a page navigation or reload.
React applications commonly prevent that default behavior so JavaScript can handle the submission.
event.preventDefault();
Then you can:
- Validate the form
- Send data to an API
- Show a loading state
- Display errors
- Show a success message
Complete Submission Example
import { useState } from "react";
function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
function handleSubmit(event) {
event.preventDefault();
console.log({
email,
password
});
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">
Email
</label>
<input
id="email"
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
/>
<label htmlFor="password">
Password
</label>
<input
id="password"
type="password"
value={password}
onChange={(event) =>
setPassword(event.target.value)
}
/>
<button type="submit">
Login
</button>
</form>
);
}
Controlled Components
A controlled component is a form element whose current value is controlled by React state.
For example:
const [email, setEmail] = useState("");
and:
<input
value={email}
onChange={(event) => setEmail(event.target.value)}
/>
React is the source of truth for the input value.
The flow looks like this:
User types
↓
onChange
↓
setState
↓
React re-renders
↓
Input receives updated value
Controlled Input Example
function SearchForm() {
const [search, setSearch] = useState("");
return (
<input
value={search}
onChange={(event) => setSearch(event.target.value)}
placeholder="Search tutorials"
/>
);
}
Now React always knows what the user has entered.
Why Use Controlled Components?
Controlled components are useful when you need to:
- Validate input while typing
- Enable or disable buttons
- Display character counts
- Show conditional UI
- Transform input
- Share form data with other components
- Submit data to APIs
- Track form state
For example:
const isValid = email.includes("@");
You can use this information immediately because React knows the current value.
Controlled Checkbox
Remember that checkboxes use checked:
<input
type="checkbox"
checked={accepted}
onChange={(event) =>
setAccepted(event.target.checked)
}
/>
Controlled Select
<select
value={category}
onChange={(event) => setCategory(event.target.value)}
>
The same principle applies.
Uncontrolled Components
An uncontrolled component allows the DOM to maintain the current form value instead of storing every value in React state.
You can use a ref to access the value when needed.
import { useRef } from "react";
function LoginForm() {
const emailRef = useRef(null);
function handleSubmit(event) {
event.preventDefault();
console.log(emailRef.current.value);
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
ref={emailRef}
/>
<button type="submit">
Submit
</button>
</form>
);
}
Here, React does not update state on every keystroke.
Instead, the DOM keeps track of the current value.
defaultValue
For an uncontrolled input, you can provide an initial value with defaultValue:
<input
type="text"
defaultValue="John"
/>
Unlike value, defaultValue sets the initial value without making React control subsequent changes.
Controlled vs Uncontrolled
| Controlled | Uncontrolled |
|---|---|
| React state stores the value | DOM stores the value |
Uses value or checked | Often uses defaultValue or defaultChecked |
Uses onChange to update state | Can use refs to read the value |
| Easy to validate while typing | Useful when direct DOM form access is convenient |
| More explicit state management | Less state code for simple cases |
For many React applications, controlled components are a straightforward default when form state needs to participate in the UI.
Uncontrolled inputs can also be useful, especially for simple forms or when integrating with APIs and libraries that work directly with the DOM.
Form Validation
Form validation checks whether user-provided information meets your application’s requirements.
For example:
- Email must be provided
- Password must be long enough
- Name cannot be empty
- Terms must be accepted
- Age must be within a valid range
HTML already provides basic browser validation.
For example:
<input
type="email"
required
/>
and:
<input
type="password"
required
minLength={8}
/>
However, React applications often need additional validation logic.
Basic React Validation
function validateForm(formData) {
const errors = {};
if (!formData.name.trim()) {
errors.name = "Name is required";
}
if (!formData.email.includes("@")) {
errors.email = "Enter a valid email address";
}
if (formData.password.length < 8) {
errors.password =
"Password must be at least 8 characters";
}
return errors;
}
You can call this function during submission.
function handleSubmit(event) {
event.preventDefault();
const validationErrors = validateForm(formData);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
// Submit valid data
}
Client-Side Validation Is Not Enough
Client-side validation improves user experience, but it should not be treated as the only security boundary.
If your application sends form data to a server, the server should validate the submitted data again.
The browser and React code are controlled by the client and cannot be trusted as the final authority.
Error Messages
Validation is most useful when users can clearly understand what needs to be corrected.
For example:
{errors.email && (
<p>{errors.email}</p>
)}
If the error exists, it is displayed.
Complete Error Example
function LoginForm() {
const [email, setEmail] = useState("");
const [error, setError] = useState("");
function handleSubmit(event) {
event.preventDefault();
if (!email.trim()) {
setError("Email is required");
return;
}
setError("");
console.log("Form submitted");
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="email">
Email
</label>
<input
id="email"
type="email"
value={email}
onChange={(event) => {
setEmail(event.target.value);
setError("");
}}
aria-invalid={Boolean(error)}
aria-describedby={
error ? "email-error" : undefined
}
/>
{error && (
<p id="email-error">
{error}
</p>
)}
<button type="submit">
Submit
</button>
</form>
);
}
The aria-invalid and aria-describedby attributes help assistive technologies understand the relationship between the input and its error message.
Multiple Errors
For larger forms, an object is useful:
const [errors, setErrors] = useState({});
For example:
{
name: "Name is required",
email: "Enter a valid email",
password: "Password is too short"
}
Then each field can display its own message.
Form Reset
After submitting a form, you may want to clear its fields.
Suppose:
const [formData, setFormData] = useState({
name: "",
email: "",
message: ""
});
You can reset it by restoring the initial object:
const initialForm = {
name: "",
email: "",
message: ""
};
setFormData(initialForm);
Reset Button
HTML provides a reset button:
<button type="reset">
Reset
</button>
However, when using controlled components, simply using the browser’s reset behavior does not update React state in the same way as changing the state itself.
For controlled forms, it is often clearer to reset the state explicitly.
function handleReset() {
setFormData(initialForm);
setErrors({});
}
Then:
<button
type="button"
onClick={handleReset}
>
Reset
</button>
Resetting After Submission
function handleSubmit(event) {
event.preventDefault();
// Validate and submit...
setFormData(initialForm);
setErrors({});
}
Whether you reset immediately depends on the application’s behavior.
For example, a contact form might clear after a successful submission, while an edit form might keep the submitted values visible.
Dynamic Forms
A dynamic form allows users to add, remove, or modify fields while the application is running.
Examples include:
- Adding multiple phone numbers
- Adding education records
- Adding work experience
- Adding product variants
- Adding multiple addresses
- Adding multiple skills
Instead of creating a fixed number of fields, you store the fields in an array.
Basic Dynamic Form
import { useState } from "react";
function SkillsForm() {
const [skills, setSkills] = useState([
{ id: 1, value: "" }
]);
return (
<div>
{skills.map((skill) => (
<input
key={skill.id}
value={skill.value}
onChange={(event) => {
setSkills((currentSkills) =>
currentSkills.map((item) =>
item.id === skill.id
? {
...item,
value: event.target.value
}
: item
)
);
}}
/>
))}
</div>
);
}
The important idea is that each field has an ID.
Adding a Field
function addSkill() {
setSkills((currentSkills) => [
...currentSkills,
{
id: crypto.randomUUID(),
value: ""
}
]);
}
Then:
<button type="button" onClick={addSkill}>
Add Skill
</button>
Each click adds another field.
Removing a Field
function removeSkill(id) {
setSkills((currentSkills) =>
currentSkills.filter((skill) => skill.id !== id)
);
}
Then:
<button
type="button"
onClick={() => removeSkill(skill.id)}
>
Remove
</button>
This creates a complete add/remove pattern.
Complete Dynamic Form
import { useState } from "react";
function SkillsForm() {
const [skills, setSkills] = useState([
{
id: crypto.randomUUID(),
value: ""
}
]);
function addSkill() {
setSkills((currentSkills) => [
...currentSkills,
{
id: crypto.randomUUID(),
value: ""
}
]);
}
function updateSkill(id, value) {
setSkills((currentSkills) =>
currentSkills.map((skill) =>
skill.id === id
? { ...skill, value }
: skill
)
);
}
function removeSkill(id) {
setSkills((currentSkills) =>
currentSkills.filter(
(skill) => skill.id !== id
)
);
}
function handleSubmit(event) {
event.preventDefault();
console.log(skills);
}
return (
<form onSubmit={handleSubmit}>
<h2>Skills</h2>
{skills.map((skill) => (
<div key={skill.id}>
<input
value={skill.value}
onChange={(event) =>
updateSkill(
skill.id,
event.target.value
)
}
placeholder="Enter a skill"
/>
<button
type="button"
onClick={() => removeSkill(skill.id)}
>
Remove
</button>
</div>
))}
<button
type="button"
onClick={addSkill}
>
Add Skill
</button>
<button type="submit">
Submit
</button>
</form>
);
}
This example demonstrates several important React concepts together:
- Array state
- Controlled inputs
map()- Stable keys
- Immutable updates
- Adding items
- Removing items
- Updating individual items
- Form submission
Dynamic Forms and Keys
Dynamic forms are another situation where keys are extremely important.
Consider:
{skills.map((skill) => (
<input
key={skill.id}
value={skill.value}
/>
))}
The key should identify the specific field.
If fields can be added, removed, or reordered, using the array index as the key can cause confusing behavior, particularly when the individual field contains local state or other identity-sensitive information.
Prefer a stable ID associated with the item:
key={skill.id}
If you create IDs yourself, make sure they remain stable for the lifetime of the item.
HTML Validation and React Validation
React forms can use both browser-level HTML validation and custom React validation.
For example:
<input
type="email"
required
/>
The browser can detect that the field is required and should contain a valid email format.
You can also add custom rules:
if (!email.endsWith("@example.com")) {
errors.email =
"Please use your company email address";
}
The right approach depends on your application.
HTML validation is useful for standard constraints.
React validation is useful for application-specific rules and custom error UI.
Server-side validation remains necessary whenever data is sent to a backend.
Loading State During Form Submission
Real applications often send form data to an API.
You may need a loading state:
const [isSubmitting, setIsSubmitting] = useState(false);
Then:
async function handleSubmit(event) {
event.preventDefault();
setIsSubmitting(true);
try {
// Send form data to API
} finally {
setIsSubmitting(false);
}
}
The button can then respond to the submission state:
<button
type="submit"
disabled={isSubmitting}
>
{isSubmitting ? "Submitting..." : "Submit"}
</button>
This prevents users from repeatedly submitting the same form while the request is being processed.
Complete React Form Example
Let’s combine the concepts into a practical registration form.
import { useState } from "react";
const initialForm = {
name: "",
email: "",
password: "",
role: "",
newsletter: false
};
function RegistrationForm() {
const [formData, setFormData] = useState(initialForm);
const [errors, setErrors] = useState({});
function handleChange(event) {
const { name, value, type, checked } =
event.target;
setFormData((currentData) => ({
...currentData,
[name]: type === "checkbox"
? checked
: value
}));
}
function validate() {
const newErrors = {};
if (!formData.name.trim()) {
newErrors.name = "Name is required";
}
if (!formData.email.includes("@")) {
newErrors.email =
"Enter a valid email address";
}
if (formData.password.length < 8) {
newErrors.password =
"Password must be at least 8 characters";
}
if (!formData.role) {
newErrors.role =
"Please select a role";
}
return newErrors;
}
function handleSubmit(event) {
event.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
setErrors({});
console.log("Submitted:", formData);
}
function handleReset() {
setFormData(initialForm);
setErrors({});
}
return (
<form onSubmit={handleSubmit}>
<label htmlFor="name">
Name
</label>
<input
id="name"
name="name"
value={formData.name}
onChange={handleChange}
/>
{errors.name && (
<p>{errors.name}</p>
)}
<label htmlFor="email">
Email
</label>
<input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && (
<p>{errors.email}</p>
)}
<label htmlFor="password">
Password
</label>
<input
id="password"
name="password"
type="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && (
<p>{errors.password}</p>
)}
<label htmlFor="role">
Role
</label>
<select
id="role"
name="role"
value={formData.role}
onChange={handleChange}
>
<option value="">
Select a role
</option>
<option value="developer">
Developer
</option>
<option value="designer">
Designer
</option>
<option value="analyst">
Data Analyst
</option>
</select>
{errors.role && (
<p>{errors.role}</p>
)}
<label>
<input
type="checkbox"
name="newsletter"
checked={formData.newsletter}
onChange={handleChange}
/>
Subscribe to newsletter
</label>
<button type="submit">
Create Account
</button>
<button
type="button"
onClick={handleReset}
>
Reset
</button>
</form>
);
}
This example demonstrates most of the important React form patterns in one component.
Image Placement: Complete React Form
Place this image before the complete React form example.
Create a modern 16:9 educational infographic titled “React Form Data Flow”. Show a polished registration form on one side and a React state object on the other. Visually show the flow Input → onChange → State → Validation → Submit. Include small visual indicators for controlled inputs, validation errors, and successful submission. Use a modern Web4Learners developer aesthetic with rounded cards, subtle gradients, clean typography, minimal text, and a contemporary UI design.
Common React Form Mistakes
Forgetting event.preventDefault()
If you handle form submission with React, remember:
function handleSubmit(event) {
event.preventDefault();
}
when you do not want the browser’s default navigation behavior.
Using value Without onChange
This can make an input effectively read-only:
<input value={name} />
For a controlled editable input, provide an update mechanism:
<input
value={name}
onChange={(event) => setName(event.target.value)}
/>
Using value for a Checkbox
For checkboxes, use:
checked={accepted}
and:
event.target.checked
rather than treating the checked state as a text value.
Mutating Form State
Avoid:
formData.email = "test@example.com";
Instead:
setFormData((currentData) => ({
...currentData,
email: "test@example.com"
}));
Forgetting name Attributes
If using one generic change handler for multiple fields, give each field a useful name:
<input
name="email"
/>
Then:
const { name, value } = event.target;
can determine which property to update.
Using Array Index as a Dynamic Form Key
Avoid:
key={index}
when dynamic items can be removed, inserted, or reordered.
Prefer:
key={item.id}
with a stable identifier.
Resetting Only the UI but Not State
If an input is controlled by React state, resetting only the DOM does not reset the React state.
Reset the state itself:
setFormData(initialForm);
Validating Only in the Browser
Browser validation is useful for user experience, but data sent to a server should also be validated on the server.
Best Practices for React Forms
Give Every Important Field a Label
Use:
<label htmlFor="email">
Email
</label>
and:
<input id="email" />
This improves accessibility and usability.
Keep Form State Organized
For related fields, an object can make state easier to manage:
const [formData, setFormData] = useState({
name: "",
email: "",
phone: ""
});
For unrelated state, separate state variables may be clearer.
Keep Validation Logic Separate
For larger forms, putting validation in a function can make the component easier to read:
function validateForm(data) {
// validation logic
}
Show Clear Error Messages
Instead of:
Invalid input
prefer a useful message such as:
Enter a valid email address.
Disable Submission When Appropriate
During an API request:
<button disabled={isSubmitting}>
This can prevent accidental duplicate submissions.
Keep State Updates Immutable
For objects:
setFormData((current) => ({
...current,
email: newEmail
}));
For arrays:
setItems((current) => [
...current,
newItem
]);
Use Stable Keys for Dynamic Fields
Stable IDs help React correctly preserve the identity of dynamic form elements.
React Forms Cheat Sheet
| Task | React Pattern |
|---|---|
| Text input | value + onChange |
| Textarea | value + onChange |
| Select | value + onChange |
| Checkbox | checked + onChange |
| Radio | checked + onChange |
| Submit form | onSubmit |
| Prevent browser submission | event.preventDefault() |
| Controlled input | React state owns the value |
| Uncontrolled input | DOM owns the value |
| Initial uncontrolled value | defaultValue |
| Initial checkbox state | defaultChecked |
| Form validation | Check values before submission |
| Error state | Store validation errors |
| Reset controlled form | Reset React state |
| Dynamic fields | Store fields in an array |
| Dynamic field identity | Use stable keys |
| API submission | Use async submission logic |
| Loading UI | Track isSubmitting |
Key Takeaways
- React forms use normal HTML form elements with React event handling and state.
- Inputs can be controlled by React state using
valueandonChange. - Checkboxes use
checkedandevent.target.checked. - Radio buttons use the same
nameto form a group. <textarea>and<select>can also be controlled through React state.- Multiple related inputs can be stored in an object.
- A generic
onChangehandler can update multiple fields using the input’sname. - Form submission is commonly handled with
onSubmit. event.preventDefault()prevents the browser’s default form navigation when appropriate.- Controlled components give React direct control over form values.
- Uncontrolled components allow the DOM to maintain their values and can be accessed with refs.
- Validation should provide useful feedback instead of simply rejecting input.
- Client-side validation improves the user experience, but server-side validation is still required for data sent to a backend.
- Error messages should be connected clearly to their corresponding fields.
- Controlled forms should reset their React state when you want to clear them.
- Dynamic forms can use arrays of objects to represent fields.
- Dynamic form items should have stable keys.
- Form submission often needs loading, success, and error states.
- Good form structure improves accessibility, maintainability, and user experience.