The forms are a fundamental part of almost any web application. They allow you to collect user information, validate data and send it to servers or APIs. In React, handling forms has certain peculiarities that make it different from traditional JavaScript.
In this tutorial, you’ll learn how forms work in React, what are controlled and uncontrolled components, how to handle inputs and how to apply validations.
In traditional HTML, forms manage their own internal state. In React, the form’s state is usually managed by the component. This allows for greater control over data, easy validation and real-time reaction to changes.
React can handle forms in two main ways:
A controlled component is one where React controls the input value through the component’s state. This is the most commonly used method as it offers more control and flexibility.
import { useState } from "react"; function Formulario() { const [nombre, setNombre] = useState(""); return ( ); }
This example:
This pattern ensures that React always has control over the data.
When a form has multiple fields, it’s common to store the data in a single object.
function Formulario() { const [formData, setFormData] = useState({ nombre: "", email: "" }); const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; return ( <form> <input name="nombre" value={formData.nombre} onChange={handleChange} placeholder="Name" /> <input name="email" value={formData.email} onChange={handleChange} placeholder="Email" /> </form> ); }
This approach allows for managing large forms in a more organized manner.
To manage the form submission, the onSubmit event is used.
const handleSubmit = (e) => { e.preventDefault(); console.log(formData); };
Usage inside the form:
<form onSubmit={handleSubmit}>
The method preventDefault prevents the page from reloading when submitting a form.
Uncontrolled components delegate state management to the DOM instead of React. To access their values, useRef is used.
import { useRef } from "react"; function Formulario() { const inputRef = useRef(); const handleSubmit = (e) => { e.preventDefault(); console.log(inputRef.current.value); }; return ( <form onSubmit={handleSubmit}> <input ref={inputRef} /> <button type="submit">Send</button> </form> ); }
This method is simpler, but offers less control over the data.
The controlled components are recommended when you need:
The uncontrolled components can be useful when:
The validation allows to check that the input data is correct before sending it.
html
const [error, setError] = useState(""); const handleSubmit = (e) => { e.preventDefault(); if (!formData.email.includes("@")) { setError("Email invalid"); return; } setError(""); };
Display error on the interface:
{error && {error}}
Real-time validation
You can also validate the data as the user types.
const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); if (name === "email" && !value.includes("@")) { setError("Email invalid"); } else { setError(""); } };
Handling different input types
Checkbox
const [activo, setActivo] = useState(false); setActivo(e.target.checked)} />
Select
const [pais, setPais] = useState("");
Radio buttons
const [genero, setGenero] = useState(""); setGenero(e.target.value)} />
Libraries for handling forms
When forms become complex, there are libraries that simplify their management.
Allows to manage forms with fewer re-renders and better performance.
Advantages:
Another popular library that simplifies the handling of forms and validations.
