Build a Simple Next.js Interactive Feedback Form

Written by

in

In today’s digital landscape, gathering user feedback is crucial for the success of any website or application. It allows you to understand your audience, identify areas for improvement, and ultimately, create a better user experience. However, building a feedback form can sometimes seem like a complex task, especially for those new to web development. This is where Next.js comes to the rescue! With its ease of use, powerful features, and focus on performance, Next.js provides an excellent platform for building interactive components, including a user-friendly feedback form. This article will guide you, step-by-step, through creating a simple, yet effective, feedback form using Next.js.

Why Build a Feedback Form?

Before diving into the code, let’s explore why incorporating a feedback form is so important. Think of it as a direct line of communication with your users. It offers several key advantages:

  • Understanding User Needs: Feedback forms allow you to directly ask users about their experience, what they like, what they dislike, and what they’d like to see improved.
  • Identifying Pain Points: Users can report bugs, usability issues, or confusing aspects of your website or app, helping you identify and fix problems quickly.
  • Improving User Experience: By analyzing feedback, you can make informed decisions to enhance the overall user experience, leading to increased user satisfaction and engagement.
  • Gathering Insights: Feedback forms can be customized to gather specific insights about user behavior, preferences, and demographics, which can inform your content strategy and product development.
  • Building a Community: A feedback form demonstrates that you value your users’ opinions and are committed to continuous improvement, fostering a sense of community.

Prerequisites

To follow along with this tutorial, you’ll need the following:

  • Basic Knowledge of HTML, CSS, and JavaScript: Familiarity with these core web technologies is essential for understanding the code and concepts.
  • Node.js and npm (or yarn) installed: You’ll need Node.js and a package manager (npm or yarn) to install and manage project dependencies.
  • A Code Editor: Choose your preferred code editor (e.g., VS Code, Sublime Text, Atom) to write and edit your code.
  • A Basic Understanding of Next.js: While this tutorial is beginner-friendly, some familiarity with Next.js concepts like components, pages, and routing will be helpful. If you’re new to Next.js, consider reviewing the official documentation or some introductory tutorials before starting.

Setting Up Your Next.js Project

Let’s get started by creating a new Next.js project. Open your terminal or command prompt and run the following command:

npx create-next-app feedback-form-app

This command will create a new Next.js project named “feedback-form-app” (you can replace this with your desired project name). Navigate into the project directory:

cd feedback-form-app

Now, start the development server:

npm run dev

or

yarn dev

This will start the development server, and you should be able to view your basic Next.js application at http://localhost:3000 (or the port specified in your terminal).

Building the Feedback Form Component

Next, we’ll create the core of our application: the feedback form component. Inside the `components` directory (create it if it doesn’t exist), create a new file named `FeedbackForm.js`.

Here’s the basic structure of the `FeedbackForm.js` component:

import React, { useState } from 'react';

function FeedbackForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [feedback, setFeedback] = useState('');
  const [submitted, setSubmitted] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    // Add your form submission logic here
  };

  return (
    <div>
      <h2>Feedback Form</h2>
      {submitted ? (
        <p>Thank you for your feedback!</p>
      ) : (
        <form onSubmit={handleSubmit}>
          <div>
            <label htmlFor="name">Name:</label>
            <input
              type="text"
              id="name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              required
            />
          </div>
          <div>
            <label htmlFor="email">Email:</label>
            <input
              type="email"
              id="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              required
            />
          </div>
          <div>
            <label htmlFor="feedback">Feedback:</label>
            <textarea
              id="feedback"
              value={feedback}
              onChange={(e) => setFeedback(e.target.value)}
              required
            />
          </div>
          <button type="submit">Submit</button>
        </form>
      )}
    </div>
  );
}

export default FeedbackForm;

Let’s break down this code:

  • Import React and useState: We import `React` and the `useState` hook to manage the form’s state.
  • State Variables: We define state variables for `name`, `email`, `feedback`, and `submitted`. `name`, `email`, and `feedback` will store the user’s input, while `submitted` will indicate whether the form has been successfully submitted.
  • handleSubmit Function: This asynchronous function is triggered when the form is submitted. Currently, it’s empty, but we’ll add the submission logic later. It prevents the default form submission behavior using `e.preventDefault()`.
  • Conditional Rendering: We use a conditional statement to display either the form or a thank-you message based on the `submitted` state.
  • Form Elements: We create the form with input fields for name and email, a textarea for the feedback, and a submit button. Each input field is bound to its corresponding state variable using the `onChange` event handler. The `required` attribute ensures the user fills out the fields.

Styling the Feedback Form

To make the form visually appealing, let’s add some basic styling. Create a CSS file named `FeedbackForm.module.css` in the same directory as `FeedbackForm.js` (i.e., inside the `components` directory). Add the following CSS:


.form-container {
  width: 80%;
  max-width: 500px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f9f9f9;
}

.form-group {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

input[type="text"],
input[type="email"],
textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 16px;
}

textarea {
  height: 150px;
  resize: vertical;
}

button {
  background-color: #4CAF50;
  color: white;
  padding: 12px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
}

button:hover {
  background-color: #45a049;
}

p {
  text-align: center;
  font-size: 18px;
  margin-top: 20px;
}

Now, import this CSS file into your `FeedbackForm.js` component and apply the styles:

import React, { useState } from 'react';
import styles from './FeedbackForm.module.css';

function FeedbackForm() {
  // ... (state variables and handleSubmit function)

  return (
    <div className={styles["form-container"]}>
      <h2>Feedback Form</h2>
      {submitted ? (
        <p>Thank you for your feedback!</p>
      ) : (
        <form onSubmit={handleSubmit}>
          <div className={styles["form-group"]}>
            <label htmlFor="name">Name:</label>
            <input
              type="text"
              id="name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              required
            />
          </div>
          <div className={styles["form-group"]}>
            <label htmlFor="email">Email:</label>
            <input
              type="email"
              id="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              required
            />
          </div>
          <div className={styles["form-group"]}>
            <label htmlFor="feedback">Feedback:</label>
            <textarea
              id="feedback"
              value={feedback}
              onChange={(e) => setFeedback(e.target.value)}
              required
            />
          </div>
          <button type="submit">Submit</button>
        </form>
      )}
    </div>
  );
}

export default FeedbackForm;

Notice how we import the CSS modules using `import styles from ‘./FeedbackForm.module.css’;` and then apply the styles using `className={styles[“form-container”]}` (and similar for other elements). This approach ensures that your CSS is scoped to the component, preventing style conflicts with other parts of your application.

Integrating the Feedback Form into a Page

Now that you’ve created the feedback form component and styled it, let’s integrate it into a page. Open the `pages/index.js` file (this is the homepage of your Next.js application) and import and render the `FeedbackForm` component:

import FeedbackForm from '../components/FeedbackForm';

function HomePage() {
  return (
    <div>
      <main>
        <FeedbackForm />
      </main>
    </div>
  );
}

export default HomePage;

Now, when you visit your homepage (usually at http://localhost:3000), you should see the feedback form rendered on the page.

Handling Form Submission

The most crucial part of the process is handling the form submission. We’ll implement the `handleSubmit` function to capture the user’s input and send it to a backend service or store it in a database. For this example, we’ll simulate sending the data to an API endpoint using the `fetch` API. Consider using a serverless function (API route) in Next.js to handle the form submission. This keeps your client-side code clean and secure.

Here’s how to modify the `handleSubmit` function in `FeedbackForm.js`:

import React, { useState } from 'react';
import styles from './FeedbackForm.module.css';

function FeedbackForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [feedback, setFeedback] = useState('');
  const [submitted, setSubmitted] = useState(false);
  const [error, setError] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError(''); // Clear any previous errors

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

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const data = await response.json();
      console.log('Success:', data);
      setSubmitted(true);
      // Optionally, reset the form fields:
      setName('');
      setEmail('');
      setFeedback('');
    } catch (err) {
      console.error('Error submitting feedback:', err);
      setError('An error occurred. Please try again.');
    }
  };

  return (
    <div className={styles["form-container"]}>
      <h2>Feedback Form</h2>
      {submitted ? (
        <p>Thank you for your feedback!</p>
      ) : (
        <form onSubmit={handleSubmit}>
          {error && <p className={styles.error}>{error}</p>}
          <div className={styles["form-group"]}>
            <label htmlFor="name">Name:</label>
            <input
              type="text"
              id="name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              required
            />
          </div>
          <div className={styles["form-group"]}>
            <label htmlFor="email">Email:</label>
            <input
              type="email"
              id="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              required
            />
          </div>
          <div className={styles["form-group"]}>
            <label htmlFor="feedback">Feedback:</label>
            <textarea
              id="feedback"
              value={feedback}
              onChange={(e) => setFeedback(e.target.value)}
              required
            />
          </div>
          <button type="submit">Submit</button>
        </form>
      )}
    </div>
  );
}

export default FeedbackForm;

In this revised code:

  • Error Handling: We’ve added an `error` state variable to display error messages to the user.
  • Fetch API: We use the `fetch` API to send the form data to an API endpoint. Replace `/api/feedback` with the actual URL of your API endpoint.
  • Method and Headers: We specify the `POST` method and set the `Content-Type` header to `application/json`.
  • Body: We stringify the form data using `JSON.stringify()` and include it in the `body` of the request.
  • Response Handling: We check the response status using `response.ok`. If the status is not in the 200-299 range, we throw an error.
  • Success Handling: If the submission is successful, we log the response data to the console, set `submitted` to `true`, and (optionally) reset the form fields.
  • Error Handling: If an error occurs during the `fetch` request or any other part of the process, we catch the error, log it to the console, and set the `error` state to display an error message to the user.

Creating the API Route (Serverless Function)

Now, let’s create the API route that will handle the form data. In Next.js, you can create API routes by creating files inside the `pages/api` directory. Create a new file named `pages/api/feedback.js` and add the following code:


export default async function handler(req, res) {
  if (req.method === 'POST') {
    try {
      const { name, email, feedback } = req.body;

      // **Important:  Add your data processing logic here.**
      // This is where you would:
      // 1.  Validate the data (e.g., check email format).
      // 2.  Save the data to a database (e.g., using Prisma, MongoDB, etc.).
      // 3.  Send an email notification.

      // For this example, we'll just log the data to the console:
      console.log('Received feedback:', { name, email, feedback });

      res.status(200).json({ message: 'Feedback received successfully' });
    } catch (error) {
      console.error('Error processing feedback:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  } else {
    // Handle any other HTTP methods
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

Key aspects of this API route:

  • Request Method Check: It checks if the request method is `POST`. If not, it returns a 405 Method Not Allowed error.
  • Data Extraction: It extracts the `name`, `email`, and `feedback` from the request body (`req.body`). The request body is automatically parsed as JSON by Next.js.
  • Data Processing (Important): This is the core of your API. You should add your logic here to process the feedback data. This could involve:

    • Data Validation: Ensure the data is in the correct format (e.g., validate the email address).
    • Data Storage: Save the data to a database (e.g., MongoDB, PostgreSQL, etc.). You’ll need to install a database client library (like `mongoose` for MongoDB or `pg` for PostgreSQL) and configure your database connection.
    • Notification: Send an email notification to yourself or an administrator when new feedback is received (e.g., using a service like SendGrid or Nodemailer).
  • Response: It sends a 200 OK response with a success message if the feedback is processed successfully, or a 500 Internal Server Error if an error occurs.

Common Mistakes and How to Fix Them

Here are some common mistakes you might encounter when building a feedback form and how to address them:

  • Incorrect Form Field Names: Make sure the `name` attributes of your input fields in the HTML match the keys you’re expecting in your API route’s `req.body`. For example, if your input field has `name=”user_name”`, your API route should access `req.body.user_name`.
  • CORS Errors: If you’re sending requests to an API on a different domain, you might encounter CORS (Cross-Origin Resource Sharing) errors. To fix this, configure CORS on your API server to allow requests from your Next.js application’s domain. In Next.js, you can use the `cors` middleware library (install it with `npm install cors`) in your API route.
  • Missing or Incorrect API Route URL: Double-check that the URL in your `fetch` request matches the correct path to your API route (e.g., `/api/feedback`).
  • Uncaught Errors in the API Route: Make sure your API route includes proper error handling (using `try…catch` blocks) to catch any exceptions and return appropriate error responses to the client.
  • Not Handling Form Submission Errors: Display error messages to the user if the form submission fails. Use the `error` state variable and display the error message in your component.
  • Forgetting to Install Dependencies: Make sure you’ve installed all the necessary dependencies (e.g., `cors`, database client libraries) using `npm install` or `yarn add`.
  • Incorrect `Content-Type` Header: Ensure you’re setting the correct `Content-Type` header in your `fetch` request to `application/json`.
  • Database Connection Issues: If you’re connecting to a database, verify your connection details (host, username, password, database name) and ensure the database server is running and accessible from your API route.

Enhancements and Next Steps

This is a basic feedback form. Here are some enhancements you can consider to make it more robust and user-friendly:

  • Input Validation: Add client-side and server-side validation to ensure the data is accurate and secure. Client-side validation can improve the user experience by providing immediate feedback. Server-side validation is essential for security.
  • CAPTCHA: Implement a CAPTCHA to prevent spam submissions.
  • Rich Text Editor: For more complex feedback, consider using a rich text editor (e.g., Draft.js, Quill) for the feedback field.
  • File Upload: Allow users to upload files (e.g., screenshots) to provide more context. You’ll need to handle file uploads on the server-side.
  • Database Integration: Connect your form to a database (e.g., MongoDB, PostgreSQL) to store the feedback data persistently.
  • Email Notifications: Send email notifications to yourself or an administrator when new feedback is received.
  • Styling and Design: Improve the form’s styling and design to match your website’s branding. Consider using a CSS framework like Tailwind CSS or Bootstrap to speed up the styling process.
  • User Authentication: If you have user accounts, allow authenticated users to submit feedback and associate the feedback with their accounts.
  • Accessibility: Ensure the form is accessible to users with disabilities by using appropriate HTML semantics, ARIA attributes, and keyboard navigation.
  • Analytics: Track form submissions and analyze the feedback data to gain insights into user behavior and improve your website or application.

Key Takeaways

Building a feedback form in Next.js is a rewarding project that combines front-end and back-end development principles. By following the steps outlined in this tutorial, you’ve learned how to create a user-friendly form, handle form submissions, and integrate it into your Next.js application. Remember to focus on data validation, error handling, and security best practices when implementing your feedback form. The knowledge you’ve gained can be applied to build a wide range of interactive components and forms, enhancing the user experience and improving your website’s functionality.

With Next.js, the possibilities are virtually limitless. You can adapt the concepts learned here to create forms for contact requests, surveys, or any other data collection need. The key is to understand the core principles of form creation, data handling, and API integration. Embrace the power of Next.js, and you’ll be well-equipped to build dynamic and engaging web applications that meet the needs of your users.