Type to generate UI components from text

OR

Browse thousands of MUI, Tailwind, React components that are fully customizable and responsive.

Explore Components

NextJS Forms: How to Make Fast and Friendly Forms

Next.js is a popular React framework for building web applications. When it comes to handling forms in Next.js, you can follow similar patterns as you would in a regular React application. Below are the steps you can take to create and handle NextJS forms.

NextJS Forms and Mutations

Data can be created and updated in web applications using forms. With API Routes, Next.js allows form submissions and data mutations to be handled efficiently.

Here are a few forms of examples with links in NextJS Forms.

Server-only formForm validation
Error handlingDisplaying loading state
RedirectingReading cookies

Create a new Next.js App

1. Let’s add a new Next.js project

You can create a new Next.js project using the following commands:

npx create-next-app my-nextjs-app
cd my-nextjs-app

Using “cd”, let’s navigate to the Next.js application directory that was just created.

2. Create a form component

Create a new file for your form component, for example, components/MyForm.js.

import { useState } from 'react';

const MyForm = () => {
  const [formData, setFormData] = useState({
    // define form fields here
    name: '',
    email: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // handle form submission logic here
    console.log('Form submitted with data:', formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
        />
      </label>
      <br />
      <label>
        Email:
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
      </label>
      <br />
      <button type="submit">Submit</button>
    </form>
  );
};

export default MyForm;

3. Use the form component in a page:

Now, you can use the MyForm component in a Next.js page. For example, in pages/index.js.

import MyForm from '../components/MyForm';

const HomePage = () => {
  return (
    <div>
      <h1>Welcome to My Next.js App</h1>
      <MyForm />
    </div>
  );
};

export default HomePage;

4. Handle form submission on the server side

If you need to handle form submissions on the server side, you can use API routes in Next.js. Create a new file under the pages/api directory, for example, pages/api/form.js and handle the form submission logic there.

// pages/api/form.js
export default function handler(req, res) {
  if (req.method === 'POST') {
    // Process the form data and send a response
    const { name, email } = req.body;
    console.log('Received form data:', { name, email });

    res.status(200).json({ message: 'Form data received successfully!' });
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

Update the form component to send a POST request to this API route upon form submission.

import React, { useState } from 'react';

const MyForm = () => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({
      ...prevData,
      [name]: value,
    }));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      const response = await fetch('/api/form', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(formData),
      });

      if (response.ok) {
        console.log('Form data submitted successfully!');
      } else {
        console.error('Failed to submit form data');
      }
    } catch (error) {
      console.error('Error submitting form data:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... (same as before) */}
    </form>
  );
};

export default MyForm;

This is a basic example, and you can customize it based on your specific requirements. Keep in mind that form handling in Next.js is similar to regular React, and you have the flexibility to choose whether you want to handle form submissions on the client side or server side.

Form Validation Using required, type, minLength, maxLength


Next.js doesn’t inherently provide built-in form validation features, as it is primarily a React framework. However, you can implement form validation in your Next.js application using JavaScript and React. Here’s an example of how you can create a form with built-in validation using the required, type, minLength, and maxLength attributes:

1. Create a new Next.js component for your form

// components/MyForm.js

import { useState } from 'react';
import './globals.css';

const MyForm = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
    setErrors({ ...errors, [name]: '' });
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    // Perform validation
    const validationErrors = validateForm(formData);
    if (Object.keys(validationErrors).length > 0) {
      setErrors(validationErrors);
    } else {
      // Form is valid, submit data or perform further actions
      console.log('Form submitted:', formData);
    }
  };

  const validateForm = (data) => {
    const errors = {};

    // Validate username
    if (!data.username.trim()) {
      errors.username = 'Username is required';
    }

    // Validate email
    if (!data.email.trim()) {
      errors.email = 'Email is required';
    } else if (!isValidEmail(data.email)) {
      errors.email = 'Invalid email address';
    }

    // Validate password
    if (data.password.length < 8) {
      errors.password = 'Password must be at least 8 characters long';
    } else if (data.password.length > 20) {
      errors.password = 'Password cannot exceed 20 characters';
    }

    return errors;
  };

  const isValidEmail = (email) => {
    // Simple email validation regex
    const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
    return emailRegex.test(email);
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Username:</label>
        <input
          type="text"
          name="username"
          value={formData.username}
          onChange={handleChange}
        />
        <span>{errors.username}</span>
      </div>

      <div>
        <label>Email:</label>
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
        />
        <span>{errors.email}</span>
      </div>

      <div>
        <label>Password:</label>
        <input
          type="password"
          name="password"
          value={formData.password}
          onChange={handleChange}
        />
        <span>{errors.password}</span>
      </div>

      <button type="submit">Submit</button>
    </form>
  );
};

export default MyForm;

2. Use the form component in your page


import MyForm from '../components/MyForm';

const HomePage = () => {
  return (
    <div>
      <h1>Next.js Form Validation Example</h1>
      <MyForm />
    </div>
  );
};

export default HomePage;

3. Simple example of CSS styles for your form

Here’s a simple example of CSS styles for your form. You can customize it further based on your design preferences.


/* styles.css (or any other stylesheet you are using) */

form {
    max-width: 400px;
    margin: auto;
    padding: 20px;
    border: 1px solid #ccc;
    border-radius: 5px;
  }
  
  div {
    margin-bottom: 15px;
  }
  
  label {
    display: block;
    font-weight: bold;
  }
  
  input {
    width: 100%;
    padding: 8px;
    margin-top: 5px;
    margin-bottom: 10px;
    box-sizing: border-box;
    border: 1px solid #ccc;
    border-radius: 4px;
  }
  
  span {
    color: red;
    font-size: 14px;
  }
  
   button {
    background-color: #4caf50;
    color: black;
    padding: 10px 15px;
    border: 1px solid black;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
  }
  
  button:hover {
    background-color: #45a049;
  }
  
  h1 {
    text-align: center;
    color: #333;
  }

This example demonstrates a basic form with validation for a username, email, and password with simple CSS for your form. Adjust the validation rules, CSS styles, and error messages based on your specific requirements.

4. Server Side handling of the form (optional)

Server-side validation and error handling are crucial to ensure the security and integrity of your application. In a Next.js application, you would typically handle form submissions by sending data to an API endpoint (server-side) for processing. Here’s a basic example demonstrating how you might set up server-side validation using a Next.js API route.

  • Create an API route for form submission

    // pages/api/form-submit.js
    
    export default async function handler(req, res) {
      if (req.method === 'POST') {
        const { username, email, password } = req.body;
    
        // Perform server-side validation
        const errors = validateFormData(username, email, password);
    
        if (Object.keys(errors).length > 0) {
          return res.status(400).json({ errors });
        }
    
        // If validation passes, process the form data (e.g., save to a database)
        // For simplicity, we'll just return a success message here
        return res.status(200).json({ message: 'Form submitted successfully!' });
      }
    
      // Handle non-POST requests
      return res.status(405).end();
    }
    
    const validateFormData = (username, email, password) => {
      const errors = {};
    
      // Add server-side validation logic here
      // For example, check if username already exists in the database, etc.
    
      return errors;
    };
  • Update your form component to make a POST request

// components/MyForm.js

import { useState } from 'react';

const MyForm = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
    setErrors({ ...errors, [name]: '' });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    // Perform client-side validation first
    const validationErrors = validateForm(formData);
    if (Object.keys(validationErrors).length > 0) {
      setErrors(validationErrors);
      return;
    }

    // Send form data to the server for further processing
    const response = await fetch('/api/form-submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(formData),
    });

    if (response.ok) {
      console.log('Form submitted successfully!');
      // You can perform additional client-side actions here
    } else {
      const responseData = await response.json();
      setErrors(responseData.errors || {});
    }
  };

  const validateForm = (data) => {
    // ... (same as previous client-side validation)
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... (rest of the form) */}
    </form>
  );
};

export default MyForm;

In this example, the form data is sent to the /api/form-submit endpoint using a POST request. The server-side code pages/api/form-submit.js performs validation and returns appropriate error messages or a success message.

Remember to adapt the server-side validation logic to your specific requirements, such as checking for duplicate usernames or verifying email formats against your database. Additionally, ensure that you handle sensitive data securely, especially when dealing with passwords.

5. Adding a loading spinner when submitting

Certainly! To add a loading spinner when submitting the form, you can modify your MyForm component to manage a loading state. Here’s an example:

// components/MyForm.js

import { useState } from 'react';

const MyForm = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const [errors, setErrors] = useState({});
  const [loading, setLoading] = useState(false); // New loading state

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
    setErrors({ ...errors, [name]: '' });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    // Perform client-side validation first
    const validationErrors = validateForm(formData);
    if (Object.keys(validationErrors).length > 0) {
      setErrors(validationErrors);
      return;
    }

    // Set loading to true when submitting
    setLoading(true);

    // Send form data to the server for further processing
    const response = await fetch('/api/form-submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(formData),
    });

    // Reset loading to false regardless of the response
    setLoading(false);

    if (response.ok) {
      console.log('Form submitted successfully!');
      // You can perform additional client-side actions here
    } else {
      const responseData = await response.json();
      setErrors(responseData.errors || {});
    }
  };

  const validateForm = (data) => {
    // ... (same as previous client-side validation)
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... (rest of the form) */}
      <button type="submit" disabled={loading}>
        {loading ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
};

export default MyForm;

In this example, a new state variable loading is introduced to track whether the form is currently being submitted. When the form is submitted, loading is set to true, and the submit button is disabled. After the form submission (successful or not), loading is set back to false. The text on the submit button is also updated to indicate the loading state.

You can enhance the loading indicator by adding an actual spinner or any visual cue that suits your design. For simplicity, I’ve used a text update on the submit button in this example.

6. Show a message once the form has been submitted

You can also display a success message once the form has been successfully submitted. Here’s an updated version of the MyForm component:

// components/MyForm.js

import { useState } from 'react';

const MyForm = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
  });

  const [errors, setErrors] = useState({});
  const [loading, setLoading] = useState(false);
  const [submitSuccess, setSubmitSuccess] = useState(false); // New state for submit success

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
    setErrors({ ...errors, [name]: '' });
    setSubmitSuccess(false); // Reset submit success message when user makes changes
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    // Perform client-side validation first
    const validationErrors = validateForm(formData);
    if (Object.keys(validationErrors).length > 0) {
      setErrors(validationErrors);
      return;
    }

    // Set loading to true when submitting
    setLoading(true);

    // Send form data to the server for further processing
    const response = await fetch('/api/form-submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(formData),
    });

    // Reset loading to false regardless of the response
    setLoading(false);

    if (response.ok) {
      console.log('Form submitted successfully!');
      // You can perform additional client-side actions here
      setSubmitSuccess(true); // Set submit success message
    } else {
      const responseData = await response.json();
      setErrors(responseData.errors || {});
    }
  };

  const validateForm = (data) => {
    // ... (same as previous client-side validation)
  };

  return (
    <form onSubmit={handleSubmit}>
      {submitSuccess && <p style={{ color: 'green' }}>Form submitted successfully!</p>}
      {/* ... (rest of the form) */}
      <button type="submit" disabled={loading}>
        {loading ? 'Submitting...' : 'Submit'}
      </button>
    </form>
  );
};

export default MyForm;

Unveiling the Essence: A Recap of NextJS Forms

Embracing Next.js for form development provides a robust foundation for building dynamic, high-performance web applications. Leveraging the power of React and the server-side rendering capabilities of Next.js, you can create seamless, interactive forms that enhance user experience and responsiveness.

Implementing NextJS form validation, whether on the client or server side, ensures data integrity and security. By combining JavaScript-based validation with server-side checks, you create a comprehensive system that not only provides a smooth user experience but also protects against potential security vulnerabilities.

As you continue to develop with Next.js, explore additional features like API routes for server-side logic, form state management with libraries like Formik or React Hook Form, and further optimizations for performance and accessibility. Visit PureCode.Ai to learn more about NextJS Forms.

Next.js offers a powerful and flexible platform for building forms within your web applications. The combination of React’s declarative syntax and Next.js’s server-side rendering capabilities provides a foundation for creating efficient and SEO-friendly forms.

Remember to strike a balance between client-side and server-side validation to ensure a seamless and secure user experience.

Integrating features such as loading spinners and success messages enhances the overall usability of your forms, providing users with feedback on their interactions. The modular structure of Next.js components allows for clean and maintainable code, making it easier to manage and extend your form functionalities as your application evolves.

Continuously explore the evolving ecosystem of Next.js, keeping an eye on updates and best practices. As you integrate forms into your Next.js projects, you’ll find that the framework’s features and flexibility empower you to deliver a rich and responsive user experience. You can learn more about NextJS Forms and related examples on PureCode.AI.

Yash Poojari

Yash Poojari