Nextjs Creating And Handling Forms In Nextjs Complete Guide
Understanding the Core Concepts of Nextjs Creating and Handling Forms in Nextjs
Creating and Handling Forms in Next.js: Detailed Guide
Step-by-Step Guide to Creating Forms in Next.js
Setting Up the Next.js Project First, set up a Next.js project if you haven't already. Run the following command in your terminal:
npx create-next-app@latest my-form-app cd my-form-app
Creating a Form Component Create a new file for your form component (e.g.,
ContactForm.js
). In this file, define a simple form structure.// pages/components/ContactForm.js import { useState } from 'react'; function ContactForm() { const [formData, setFormData] = useState({ name: '', email: '', message: '', }); const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value, }); }; const handleSubmit = async (e) => { e.preventDefault(); try { const res = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); if (res.status === 200) { alert('Message sent successfully'); } else { alert('Error sending message'); } } catch (err) { console.error(err); } setFormData({ name: '', email: '', message: '' }); }; return ( <form onSubmit={handleSubmit}> <div> <label>Name:</label> <input type="text" name="name" value={formData.name} onChange={handleChange} required /> </div> <div> <label>Email:</label> <input type="email" name="email" value={formData.email} onChange={handleChange} required /> </div> <div> <label>Message:</label> <textarea name="message" value={formData.message} onChange={handleChange} required /> </div> <button type="submit">Send</button> </form> ); } export default ContactForm;
Using the Form Component Import and use the
ContactForm
component within any page (e.g.,index.js
).// pages/index.js import Head from 'next/head'; import ContactForm from '../components/ContactForm'; export default function Home() { return ( <div> <Head> <title>Contact Us</title> </Head> <main> <h1>Contact Us</h1> <ContactForm /> </main> </div> ); }
Creating an API Endpoint Next, let's create an API endpoint to handle the form submission. Create a new file
contact.js
inside thepages/api
directory.
Online Code run
Step-by-Step Guide: How to Implement Nextjs Creating and Handling Forms in Nextjs
Table of Contents
- Introduction to Forms in Next.js
- Setting Up Your Next.js Project
- Creating a Basic Form
- Handling Form Submission
- Styling the Form
- Adding Form Validation
- Conclusion
1. Introduction to Forms in Next.js
Forms are essential in building interactive web applications. They allow users to input data and send it to the server for processing. In Next.js, handling forms can be done using React's state management and the built-in components provided by Next.js.
2. Setting Up Your Next.js Project
First, you need to set up a new Next.js project. If you already have a project set up, you can skip this step.
Step-by-Step:
Create a New Project
Open your terminal and run the following command to create a new Next.js project:
npx create-next-app@latest my-nextjs-form-app
Choose your preferred options when prompted.
Navigate to the Project Directory
cd my-nextjs-form-app
Run the Development Server
npm run dev
This will start the development server, and you can view your project at
http://localhost:3000
.
3. Creating a Basic Form
In this step, we'll create a simple form with some input fields.
Step-by-Step:
Create a New Page for the Form
Go to the
pages
directory and create a new file namedform.js
.Add Some Basic Input Fields
Open
form.js
and add the following code:import { useState } from 'react'; const FormPage = () => { const [formData, setFormData] = useState({ name: '', email: '', message: '', }); const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value, }); }; return ( <div style={{ padding: '20px' }}> <h1>Contact Form</h1> <form> <div> <label htmlFor="name">Name:</label> <input type="text" id="name" name="name" value={formData.name} onChange={handleChange} required /> </div> <div> <label htmlFor="email">Email:</label> <input type="email" id="email" name="email" value={formData.email} onChange={handleChange} required /> </div> <div> <label htmlFor="message">Message:</label> <textarea id="message" name="message" value={formData.message} onChange={handleChange} required /> </div> <button type="submit">Submit</button> </form> </div> ); }; export default FormPage;
Access the Form Page
Open your browser and navigate to
http://localhost:3000/form
. You should see the contact form with fields for name, email, and message.
4. Handling Form Submission
Now, let's add functionality to handle form submissions and display a success message.
Step-by-Step:
Create a Function to Handle Form Submission
Update
form.js
to include ahandleSubmit
function:import { useState } from 'react'; const FormPage = () => { const [formData, setFormData] = useState({ name: '', email: '', message: '', }); const [submitted, setSubmitted] = useState(false); const [successMessage, setSuccessMessage] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value, }); }; const handleSubmit = async (e) => { e.preventDefault(); // Prevent the default form submission behavior setSubmitted(true); setSuccessMessage(''); setErrorMessage(''); try { const response = await fetch('/api/submit-form', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); const result = await response.json(); if (response.ok) { setSuccessMessage(result.message); setFormData({ name: '', email: '', message: '' }); // Clear form fields } else { setErrorMessage(result.message); } } catch (error) { setErrorMessage('An error occurred. Please try again later.'); } finally { setSubmitted(false); } }; return ( <div style={{ padding: '20px' }}> <h1>Contact Form</h1> {successMessage && ( <p style={{ color: 'green' }}>{successMessage}</p> )} {errorMessage && <p style={{ color: 'red' }}>{errorMessage}</p>} <form onSubmit={handleSubmit}> <div> <label htmlFor="name">Name:</label> <input type="text" id="name" name="name" value={formData.name} onChange={handleChange} required /> </div> <div> <label htmlFor="email">Email:</label> <input type="email" id="email" name="email" value={formData.email} onChange={handleChange} required /> </div> <div> <label htmlFor="message">Message:</label> <textarea id="message" name="message" value={formData.message} onChange={handleChange} required /> </div> <button type="submit" disabled={submitted}> {submitted ? 'Submitting...' : 'Submit'} </button> </form> </div> ); }; export default FormPage;
Create an API Endpoint to Handle Form Data
Create a file named
submit-form.js
inside thepages/api
directory:mkdir pages/api touch pages/api/submit-form.js
Add Code to Handle POST Requests
Open
submit-form.js
and add the following code:export default function handler(req, res) { if (req.method === 'POST') { const { name, email, message } = req.body; if (!name || !email || !message) { return res.status(400).json({ message: 'All fields are required.' }); } // Here you can add code to save the form data to a database, send an email, etc. console.log('Form Data:', req.body); return res.status(200).json({ message: 'Form submitted successfully.' }); } else { res.setHeader('Allow', ['POST']); return res.status(405).json({ message: `Method ${req.method} not allowed` }); } }
Test the Form Submission
Go back to
http://localhost:3000/form
and fill out the form. When you submit it, the data should be logged to the terminal where your Next.js server is running, and you should see a success message on the page.
5. Styling the Form
Let's add some basic styles to make the form look better.
Step-by-Step:
Open
form.js
and Add StylesYou can add inline styles, a CSS file, or use a CSS-in-JS library. Here, we'll add inline styles for simplicity:
import { useState } from 'react'; const FormPage = () => { const [formData, setFormData] = useState({ name: '', email: '', message: '', }); const [submitted, setSubmitted] = useState(false); const [successMessage, setSuccessMessage] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const handleChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value, }); }; const handleSubmit = async (e) => { e.preventDefault(); setSubmitted(true); setSuccessMessage(''); setErrorMessage(''); try { const response = await fetch('/api/submit-form', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData), }); const result = await response.json(); if (response.ok) { setSuccessMessage(result.message); setFormData({ name: '', email: '', message: '' }); } else { setErrorMessage(result.message); } } catch (error) { setErrorMessage('An error occurred. Please try again later.'); } finally { setSubmitted(false); } }; return ( <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}> <h1 style={{ textAlign: 'center' }}>Contact Form</h1> {successMessage && ( <p style={{ color: 'green', textAlign: 'center' }}>{successMessage}</p> )} {errorMessage && ( <p style={{ color: 'red', textAlign: 'center' }}>{errorMessage}</p> )} <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '10px', }} > <div> <label htmlFor="name" style={{ marginBottom: '5px' }}> Name: </label> <input type="text" id="name" name="name" value={formData.name} onChange={handleChange} required style={{ padding: '8px', fontSize: '16px', border: '1px solid #ccc', borderRadius: '4px', width: '100%', }} /> </div> <div> <label htmlFor="email" style={{ marginBottom: '5px' }}> Email: </label> <input type="email" id="email" name="email" value={formData.email} onChange={handleChange} required style={{ padding: '8px', fontSize: '16px', border: '1px solid #ccc', borderRadius: '4px', width: '100%', }} /> </div> <div> <label htmlFor="message" style={{ marginBottom: '5px' }}> Message: </label> <textarea id="message" name="message" value={formData.message} onChange={handleChange} required style={{ padding: '8px', fontSize: '16px', border: '1px solid #ccc', borderRadius: '4px', width: '100%', resize: 'vertical', minHeight: '100px', }} /> </div> <button type="submit" disabled={submitted} style={{ padding: '10px 15px', fontSize: '16px', backgroundColor: '#0070f3', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', opacity: submitted ? 0.7 : 1, }} > {submitted ? 'Submitting...' : 'Submit'} </button> </form> </div> ); }; export default FormPage;
View the Styled Form
Refresh the form page at
http://localhost:3000/form
. The form should now look more visually appealing.
6. Adding Form Validation
To enhance the form, we can add client-side validation using a library like react-hook-form
.
Step-by-Step:
Install
react-hook-form
Run the following command to install the library:
npm install react-hook-form
Update
form.js
to Usereact-hook-form
Replace the existing code in
form.js
with the following:import { useState } from 'react'; import { useForm } from 'react-hook-form'; const FormPage = () => { const [formData, setFormData] = useState(null); const [submitted, setSubmitted] = useState(false); const [successMessage, setSuccessMessage] = useState(''); const [errorMessage, setErrorMessage] = useState(''); const { register, handleSubmit, formState: { errors }, reset, } = useForm(); const onSubmit = async (data) => { setSubmitted(true); setSuccessMessage(''); setErrorMessage(''); try { const response = await fetch('/api/submit-form', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); const result = await response.json(); if (response.ok) { setSuccessMessage(result.message); reset(); } else { setErrorMessage(result.message); } } catch (error) { setErrorMessage('An error occurred. Please try again later.'); } finally { setSubmitted(false); } }; return ( <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}> <h1 style={{ textAlign: 'center' }}>Contact Form</h1> {successMessage && ( <p style={{ color: 'green', textAlign: 'center' }}>{successMessage}</p> )} {errorMessage && ( <p style={{ color: 'red', textAlign: 'center' }}>{errorMessage}</p> )} <form onSubmit={handleSubmit(onSubmit)} style={{ display: 'flex', flexDirection: 'column', gap: '10px', }} > <div> <label htmlFor="name" style={{ marginBottom: '5px' }}> Name: {errors.name && ( <span style={{ color: 'red', marginLeft: '5px' }}> {errors.name.message} </span> )} </label> <input type="text" id="name" {...register('name', { required: 'Name is required' })} style={{ padding: '8px', fontSize: '16px', border: '1px solid #ccc', borderRadius: '4px', width: '100%', borderColor: errors.name ? 'red' : 'initial', }} /> </div> <div> <label htmlFor="email" style={{ marginBottom: '5px' }}> Email: {errors.email && ( <span style={{ color: 'red', marginLeft: '5px' }}> {errors.email.message} </span> )} </label> <input type="email" id="email" {...register('email', { required: 'Email is required', pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: 'Invalid email address', }, })} style={{ padding: '8px', fontSize: '16px', border: '1px solid #ccc', borderRadius: '4px', width: '100%', borderColor: errors.email ? 'red' : 'initial', }} /> </div> <div> <label htmlFor="message" style={{ marginBottom: '5px' }}> Message: {errors.message && ( <span style={{ color: 'red', marginLeft: '5px' }}> {errors.message.message} </span> )} </label> <textarea id="message" {...register('message', { required: 'Message is required' })} style={{ padding: '8px', fontSize: '16px', border: '1px solid #ccc', borderRadius: '4px', width: '100%', resize: 'vertical', minHeight: '100px', borderColor: errors.message ? 'red' : 'initial', }} /> </div> <button type="submit" disabled={submitted} style={{ padding: '10px 15px', fontSize: '16px', backgroundColor: '#0070f3', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer', opacity: submitted ? 0.7 : 1, }} > {submitted ? 'Submitting...' : 'Submit'} </button> </form> </div> ); }; export default FormPage;
Test the Validation
Navigate to
http://localhost:3000/form
and try submitting the form without filling out the fields. You should see validation error messages next to each field.
7. Conclusion
In this tutorial, we learned how to create and handle forms in Next.js. We covered:
- Setting up a Next.js project
- Creating a basic form with HTML elements
- Handling form submissions using React state and API endpoints
- Styling the form using inline styles
- Adding client-side validation using
react-hook-form
This should give you a solid foundation for building more complex and robust forms in Next.js. Happy coding!
Top 10 Interview Questions & Answers on Nextjs Creating and Handling Forms in Nextjs
1. How do I create a basic form in Next.js?
Answer: Creating a basic form in Next.js is similar to React due to Next.js's reliance on the React library. You can create form fields like inputs, textareas, and select elements within a form tag.
// pages/create-form.js
import { useState } from 'react';
const CreateForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
console.log('Name:', name);
console.log('Email:', email);
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<label>
Email:
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<button type="submit">Submit</button>
</form>
);
};
export default CreateForm;
2. Can I use server-side rendering (SSR) for forms in Next.js?
Answer: While SSR renders your page initially on the server and then on the client, you can handle form submissions either on the client side (CSR) or through an API route (SSR). For basic client-side validations, it’s more efficient to do them on the CSR, as they don’t require server interaction. However, complex validations and data processing should be done using API routes which can leverage SSR for better performance.
// pages/form-submit.js
export async function getServerSideProps(context) {
// Process the request here if needed before serving the page.
return { props: {} };
}
const FormSubmitPage = ({}) => {
// Handle form submission and send data to the server here.
};
export default FormSubmitPage;
3. What are some best practices for handling form state in Next.js?
Answer: Managing form state effectively can improve application performance. Consider these practices:
- Use
useState
oruseReducer
: These hooks help manage form state locally within components. - Controlled Components: Ensure that all input fields are controlled by setting initial values and creating handlers for changes.
- Debounce for Inputs: Use debouncing to limit the rate of state updates (e.g., while typing in search).
- Validation: Implement validations both client-side and server-side to catch errors early and securely.
4. How can I submit forms using serverless functions in Next.js?
Answer: When form data needs to be processed, you can connect the form to serverless functions (API routes) by making fetch requests. API routes allow server-side code that doesn’t get bundled with client-side code and only run on the server.
Create an API route by adding a file inside pages/api
. For example:
// pages/api/submit-form.js
export default function handler(req, res) {
if (req.method === 'POST') {
// Process a POST request
// req.body will have the form submission data
const data = req.body;
// Do something with it, such as saving to a database
console.log('Form data:', data);
res.status(200).json({ message: 'Form submitted successfully!' });
} else {
// Return a response for other HTTP methods (like GET)
res.setHeader('Allow', ['POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
The form can submit data to this API route like follows:
// pages/form.js
import { useState } from 'react';
const CreateForm = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
const formData = {
name,
email,
message,
};
const response = await fetch('/api/submit-form', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
const result = await response.json();
setMessage(result.message);
};
return (
<div>
<form onSubmit={handleSubmit}>
{/* ... */}
</form>
<p>{message}</p>
</div>
);
};
export default CreateForm;
5. Can I use third-party libraries for forms in Next.js?
Answer: Yes, Next.js supports various third-party form libraries such as React Hook Form, Formik, and Final Form. They make state management, validation, and error handling much easier.
Using React Hook Form Example:
// pages/rhf-form.js
import { useForm } from 'react-hook-form';
const RhfForm = () => {
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>
First Name:
<input name="firstName" ref={register({ required: true })} />
</label>
{errors.firstName && <span>This field is required</span>}
{/* ... */}
<input type="submit" />
</form>
);
};
export default RhfForm;
6. Is it possible to use CSS-in-JS libraries for styling forms in Next.js?
Answer: Yes, Next.js works well with CSS-in-JS libraries such as styled-components, emotion, and styled-jsx (built into Next.js).
Example with Emotion:
First, install emotion:
npm install @emotion/css @emotion/react
Then use it:
// pages/emotion-form.js
import styled from '@emotion/styled';
import { useForm } from 'react-hook-form';
const StyledForm = styled.form`
max-width: 500px;
margin: 0 auto;
padding: 1em;
input[type='text'],
input[type='email'] {
padding: 0.5em;
display: inline-block;
border-radius: 0.25rem;
width: 100%;
box-sizing: border-box;
}
label {
padding: 0.5em;
color: #333333;
display: block;
}
button {
padding: 1em;
background-color: #007BFF;
color: white;
border: none;
border-radius: 0.25rem;
cursor: pointer;
}
`;
const FormWithEmotion = () => {
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<StyledForm onSubmit={handleSubmit(onSubmit)}>
<label>
Name:
<input name="firstName" ref={register} />
</label>
{/* ... */}
<button type="submit">Submit</button>
</StyledForm>
);
};
export default FormWithEmotion;
7. How do I handle file uploads in Next.js?
Answer: To handle file uploads in Next.js, you should configure an API route to accept file data. Since built-in body parsers in Next.js cannot parse multipart bodies directly, you can use external packages such as busboy
or multer
.
Here’s how to set up file uploads with formidable
:
First, install formidable
:
npm install formidable
Then create an API route:
// pages/api/upload.js
import formidable from 'formidable';
const handler = async (req, res) => {
const form = new formidable.IncomingForm();
if (req.method === 'POST') {
form.parse(req, (err, fields, files) => {
if (err) return res.status(400).json({ message: err });
// Do something with the files and fields.
res.status(200).json({ message: 'Success', fields, files });
});
} else {
res.setHeader('Allow', ['POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
};
export const config = {
api: {
bodyParser: false, // Disallow body parsing, consume as stream
},
};
export default handler;
In the frontend, use a form with enctype="multipart/form-data"
and input elements with type file
:
// pages/file-upload.js
import { useState } from 'react';
const FileUploadPage = () => {
const [status, setStatus] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const res = await fetch('/api/upload', {
method: 'POST',
body: formData,
});
const result = await res.json();
setStatus(result.message);
};
return (
<div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<input
name="photo"
type="file"
accept="image/*"
className="input"
/>
<button type="submit">Upload Photo</button>
</form>
<p>{status}</p>
</div>
);
};
export default FileUploadPage;
8. How can I validate forms in Next.js without any external libraries?
Answer: You can manually validate inputs through useState and useEffect hooks without external validation libraries.
// pages/manual-form-validation.js
import { useState } from 'react';
const ManualValidationForm = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
});
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
};
const validate = () => {
const newErrors = {};
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!formData.name) {
newErrors.name = 'Name is required.';
}
if (!formData.email) {
newErrors.email = 'Email is required.';
} else if (!emailRegex.test(formData.email)) {
newErrors.email = 'Email must be a valid address.';
}
setErrors(newErrors);
return Object.keys(newErrors).length > 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validate()) {
// No errors, proceed with submission.
console.log('Form submitted:', formData);
} else {
console.log('Form contains errors.', errors);
}
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input name="name" value={formData.name} onChange={handleChange} />
{errors.name ? <small style={{ color: 'red' }}>{errors.name}</small> : null}
</label>
<label>
Email:
<input name="email" value={formData.email} onChange={handleChange} />
{errors.email ? <small style={{ color: 'red' }}>{errors.email}</small> : null}
</label>
<button type="submit">Submit</button>
</form>
);
};
export default ManualValidationForm;
9. What is the difference between client-side and server-side form validation in Next.js?
Answer: Both client-side and server-side validations are essential to ensure the integrity of data submitted via forms but serve different purposes:
Client-Side Validation: Performed on the user's browser before data is sent to the server. It helps reduce server loads by catching errors immediately, improving user experience via real-time feedback. Use it for checks like field presence, format, etc.
Server-Side Validation: Ensures that all submitted data is validated in a secure environment to prevent malicious attacks. Even if client-side validations fail, server-side validation acts as a safeguard. It’s crucial for business rules and data constraints.
10. How to implement form submission handling with success and error messages in Next.js?
Answer: In order to inform users about the status of their form submission, you can maintain state variables for success and error messages, and update these variables based on the outcome of the form submission API call.
Here’s an example:
Login to post a comment.