ReactJS Forms in Detail
Building forms is one of the most common yet crucial tasks in any web application—and ReactJS provides multiple ways to tackle it. Whether you’re creating a simple contact form or a complex multi-step input system, mastering how forms work in React is essential. In this guide, we’ll explore practical and modern approaches to building forms using React. We’ll start with the classic useState
method, move to useRef
for direct DOM manipulation, and finish with the powerful react-hook-form
library that simplifies validation and state handling.
useState
, useRef
, and also a form library like react-hook-form
.
useState
, useRef
, and also a form library like react-hook-form
.
1. Basic React Form using useState
<import React from "react">
export default function App() {
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const [acceptedTerms, setAcceptedTerms] = React.useState(false);
const handleSubmit = (event) => {
console.log(`\nEmail: ${email}\nPassword: ${password}\nAccepted Terms: ${acceptedTerms}`);
event.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<h1>Create Account</h1>
<label>Email:
<input name="email" type="email" value={email} onChange={e => setEmail(e.target.value)} required />
</label>
<label>Password:
<input name="password" type="password" value={password} onChange={e => setPassword(e.target.value)} required />
</label>
<label>
<input name="acceptedTerms" type="checkbox" onChange={e => setAcceptedTerms(e.target.checked)} required />
I accept the terms of service
</label>
<button>Submit</button>
</form>
);
}
2. Using Dynamic Keys with useState (Spread Operator)
<import React from "react">
const App = () => {
const [formData, updateFormData] = React.useState({});
const handleChange = (e) => {
updateFormData({
...formData,
[e.target.name]: e.target.value.trim()
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
};
return (
<>
<label>Username <input name="username" onChange={handleChange} /></label><br />
<label>Password <input name="password" onChange={handleChange} /></label><br />
{JSON.stringify(formData)}
<button onClick={handleSubmit}>Submit</button>
</>
);
};
export default App;
Q: Why do we write [e.target.name]
in square brackets?
This is called computed property syntax. It allows us to dynamically set the object key based on the input’s name. This helps reuse one onChange
handler for multiple fields.
3. Using useRef
for Forms
<import React from "react">
const App = () => {
const usernameRef = React.useRef();
const passwordRef = React.useRef();
const handleSubmit = () => {
console.log(usernameRef.current.value, passwordRef.current.value);
};
return (
<>
<label>Username <input ref={usernameRef} /></label><br />
<label>Password <input ref={passwordRef} /></label><br />
<button onClick={handleSubmit}>Submit</button>
</>
);
};
export default App;
4. Form using react-hook-form
<import React from 'react'>
<import { useForm } from 'react-hook-form'>
export default function App() {
const {
register,
handleSubmit,
watch,
formState: { errors },
} = useForm();
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('firstName', {
required: 'You must enter First Name',
minLength: { value: 8, message: 'Minimum 8 characters' }
})} />
{errors.firstName && <span>{errors.firstName.message}</span>}
<input {...register('lastName', { required: true })} />
{errors.lastName && <span>Last name is required</span>}
<input {...register('Email', {
required: 'Please enter email',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address'
}
})} />
{errors.Email && <span>{errors.Email.message}</span>}
<input type="submit" />
</form>
);
}
5. Additional Validated Form Example
<import React from 'react'>
<import { useForm } from 'react-hook-form'>
function MyForm() {
const {
register,
handleSubmit,
formState: { errors, isDirty, isValid },
} = useForm({ mode: 'onChange' });
const onSubmit = (data) => console.log('Form Data:', data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>Name:
<input {...register('name', { required: 'Name is required' })} type="text" />
{errors.name && <p>{errors.name.message}</p>}
</label>
<label>Email:
<input {...register('email', {
required: 'Email is required',
pattern: { value: /^\S+@\S+$/i, message: 'Invalid email' }
})} type="email" />
{errors.email && <p>{errors.email.message}</p>}
</label>
<label>Age:
<input {...register('age', {
required: 'Age is required',
validate: (value) => value > 0 || 'Must be positive number'
})} type="number" />
{errors.age && <p>{errors.age.message}</p>}
</label>
<p>Form is {isDirty ? 'dirty' : 'not dirty'}</p>
<p>Form is {isValid ? 'valid' : 'not valid'}</p>
<button type="submit" disabled={!isDirty || !isValid}>Submit</button>
</form>
);
}
export default MyForm;
Assignments
- Create a form without using the spread operator with fields like Product Name, Category, Price, etc.
- Redo the form using spread operator and dynamic state update
- Finally, build the same form using
react-hook-form
with validations
Summary
React makes form handling declarative and powerful, especially when combined with hooks like useState
, useRef
, and third-party tools like react-hook-form
. This tutorial demonstrated how to manage form data through individual states, dynamic keys, and references. It also introduced efficient ways to simplify form logic, reuse handlers, and improve validation using libraries.
react-hook-form
not only reduces boilerplate code but also ensures better performance and cleaner validation flows. As assignments, readers were encouraged to build forms using both basic React techniques and form libraries. Whether you’re building login pages, product submission forms, or dashboards, these techniques will help you create more robust and scalable ReactJS applications.