Table of Content#
- Basic Form Handling in Next.js
- Client - Side Validation
- Server - Side Validation
- Using Third - Party Libraries for Validation
- Best Practices
1. Basic Form Handling in Next.js#
In Next.js, you can handle forms using the built - in useState hook (for simple forms) or more advanced libraries.
Example#
import React, { useState } from'react';
const MyForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
// Here you can send the data to an API or perform other actions
console.log({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
);
};
export default MyForm;2. Client - Side Validation#
Client - side validation is used to provide immediate feedback to the user.
Common Practices#
- Using HTML5 Validation Attributes: For example,
requiredfor required fields,patternfor regex - based validation.
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
/>- Using JavaScript for Custom Validation: You can write custom functions to validate more complex scenarios.
const validateForm = () => {
let isValid = true;
if (name === '') {
isValid = false;
// You can show an error message here
}
if (!email.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/)) {
isValid = false;
}
return isValid;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validateForm()) {
// Proceed with form submission
}
};3. Server - Side Validation#
Server - side validation is crucial as client - side validation can be bypassed. In Next.js, you can use API routes for server - side validation.
Example#
Create an API route (pages/api/submit-form.js):
export default function handler(req, res) {
const { name, email } = req.body;
let errors = {};
if (!name) {
errors.name = 'Name is required';
}
if (!email.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/)) {
errors.email = 'Invalid email';
}
if (Object.keys(errors).length > 0) {
res.status(400).json({ errors });
} else {
// Here you can save the data to a database etc.
res.status(200).json({ message: 'Form submitted successfully' });
}
}And in the form component:
import React, { useState } from'react';
import axios from 'axios';
const MyForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [errors, setErrors] = useState({});
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('/api/submit-form', { name, email });
console.log(response.data);
} catch (error) {
if (error.response) {
setErrors(error.response.data.errors);
}
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
{errors.name && <p>{errors.name}</p>}
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && <p>{errors.email}</p>}
<button type="submit">Submit</button>
</form>
);
};
export default MyForm;4. Using Third - Party Libraries for Validation#
Yup#
Yup is a popular validation library.
Example#
Install Yup: npm install yup
import React, { useState } from'react';
import * as Yup from 'yup';
import axios from 'axios';
const schema = Yup.object().shape({
name: Yup.string().required('Name is required'),
email: Yup.string().email('Invalid email').required('Email is required')
});
const MyForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [errors, setErrors] = useState({});
const handleSubmit = async (e) => {
e.preventDefault();
try {
await schema.validate({ name, email });
const response = await axios.post('/api/submit - form', { name, email });
console.log(response.data);
} catch (error) {
if (error.inner) {
const validationErrors = {};
error.inner.forEach((err) => {
validationErrors[err.path] = err.message;
});
setErrors(validationErrors);
}
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
{errors.name && <p>{errors.name}</p>}
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
{errors.email && <p>{errors.email}</p>}
<button type="submit">Submit</button>
</form>
);
};
export default MyForm;5. Best Practices#
- Separate Concerns: Keep form handling, validation (client and server - side), and UI components separate as much as possible.
- Provide Clear Error Messages: Make sure error messages are user - friendly and easy to understand.
- Use Loading States: When submitting forms (especially when making API calls), show a loading state to the user.
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
try {
// Form submission logic
} catch (error) {
// Error handling
} finally {
setIsLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
{/* Form elements */}
{isLoading && <p>Submitting...</p>}
<button type="submit" disabled={isLoading}>Submit</button>
</form>
);