Build a Next.js Interactive Web-Based Feedback Widget

Written by

in

In the digital age, gathering feedback is crucial for the success of any online venture. Whether you’re running a blog, an e-commerce store, or a SaaS platform, understanding your users’ experiences is paramount. While traditional methods like email surveys and contact forms exist, they often lack the immediacy and user-friendliness needed to capture valuable insights. This is where a well-designed feedback widget comes in. It provides a seamless way for users to share their thoughts, suggestions, and issues directly within your application, empowering you to make data-driven decisions and improve your product or service.

Why Build a Feedback Widget with Next.js?

Next.js, a React framework for production, offers several advantages for building a feedback widget:

  • Performance: Next.js applications are optimized for speed, providing a smooth and responsive user experience.
  • SEO: Next.js supports server-side rendering (SSR) and static site generation (SSG), making your widget easily discoverable by search engines.
  • Developer Experience: Next.js provides a streamlined development experience with features like hot module replacement and built-in routing.
  • Scalability: Next.js is designed to handle large-scale applications, ensuring your feedback widget can grow with your needs.

This tutorial will guide you through building a fully functional feedback widget using Next.js, covering everything from setting up your development environment to deploying your widget.

Setting Up Your Development Environment

Before we dive into the code, let’s set up our development environment. You’ll need Node.js and npm (or yarn) installed on your machine. If you don’t have them, download and install them from the official Node.js website.

Once you have Node.js and npm installed, create a new Next.js project using the following command in your terminal:

npx create-next-app feedback-widget

This command will create a new directory called feedback-widget with the basic structure of a Next.js application. Navigate into the project directory:

cd feedback-widget

Now, start the development server:

npm run dev

Open your browser and go to http://localhost:3000. You should see the default Next.js welcome page. This confirms that your development environment is set up correctly.

Project Structure and Dependencies

Let’s take a look at the project structure. The basic structure created by `create-next-app` looks like this:

feedback-widget/
├── node_modules/
├── package.json
├── README.md
├── public/
│   └── ...
├── src/
│   ├── app/
│   │   ├── globals.css
│   │   ├── layout.js
│   │   └── page.js
│   └── ...
└── ...

We’ll be working primarily within the src/app directory. We’ll need a few dependencies to build our feedback widget. Install them using npm or yarn:

npm install react-icons react-hook-form axios

Here’s a breakdown of the dependencies:

  • react-icons: For easily importing icons.
  • react-hook-form: For managing form state and validation.
  • axios: For making API requests to submit feedback.

Building the Feedback Form Component

Let’s create the core of our feedback widget: the form. We’ll create a new component called FeedbackForm.js in the src/app directory. This component will handle the form’s UI, user input, and submission.

Create a new file named FeedbackForm.js inside the src/app directory and add the following code:

import React from 'react';
import { useForm } from 'react-hook-form';
import { FaComment, FaStar } from 'react-icons/fa';
import axios from 'axios';

const FeedbackForm = () => {
  const { register, handleSubmit, formState: { errors }, reset } = useForm();

  const onSubmit = async (data) => {
    try {
      await axios.post('/api/feedback', data); // Replace with your API endpoint
      alert('Thank you for your feedback!');
      reset(); // Clear the form after successful submission
    } catch (error) {
      console.error('Error submitting feedback:', error);
      alert('An error occurred. Please try again.');
    }
  };

  return (
    <div className="feedback-form">
      <h2>We Value Your Feedback</h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="form-group">
          <label htmlFor="rating">Rating:</label>
          <div className="stars">
            <FaStar className="star" />
            <FaStar className="star" />
            <FaStar className="star" />
            <FaStar className="star" />
            <FaStar className="star" />
          </div>
          </div>
        <div className="form-group">
          <label htmlFor="comment">Comments:</label>
          <textarea
            id="comment"
            {...register("comment", { required: true })}
            placeholder="Share your thoughts..."
          />
          {errors.comment && <span className="error">Comment is required</span>}
        </div>
        <button type="submit">Submit Feedback <FaComment /></button>
      </form>
    </div>
  );
};

export default FeedbackForm;

Let’s break down this code:

  • Imports: We import necessary modules: useForm from react-hook-form for form management, icons from react-icons, and axios for API calls.
  • useForm Hook: The useForm hook from react-hook-form handles form state and validation.
  • onSubmit Function: This function is called when the form is submitted. It sends the form data to an API endpoint (we’ll create this later) and displays a success or error message. The `reset()` function clears the form after a successful submission.
  • JSX Structure: The component renders a simple form with a rating (using placeholder stars for now) and a comment field. It uses `register` to connect form fields to react-hook-form and handles errors.
  • Error Handling: Basic error handling is implemented to display a message if the comment field is empty.

Styling the Feedback Form

To make the form visually appealing, let’s add some basic styles. Open src/app/globals.css and add the following CSS:


.feedback-form {
  width: 100%;
  max-width: 400px;
  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;
}

textarea {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 16px;
  resize: vertical;
}

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

button:hover {
  background-color: #3e8e41;
}

.error {
  color: red;
  font-size: 0.8em;
}

.stars {
  display: flex;
  gap: 5px;
  margin-bottom: 10px;
}

.star {
  color: #ddd;
  font-size: 20px;
  cursor: pointer;
}

.star.active {
  color: #ffc107;
}

This CSS provides basic styling for the form, including margins, padding, colors, and font sizes. You can customize these styles to match your application’s design.

Integrating the Feedback Form into Your Application

Now, let’s integrate the FeedbackForm component into your main page. Open src/app/page.js and modify it as follows:

import React from 'react';
import FeedbackForm from './FeedbackForm';

const Home = () => {
  return (
    <div>
      <main>
        <FeedbackForm />
      </main>
    </div>
  );
};

export default Home;

This imports the FeedbackForm component and renders it within the main content area of your home page. Now, when you visit your application in the browser (http://localhost:3000), you should see the feedback form.

Creating the API Endpoint (Backend Logic)

The feedback form currently submits data, but we haven’t created the backend logic to handle it. Next.js provides a convenient way to create API routes. We’ll create an API endpoint to receive and process the feedback data. Create a new directory called api inside the src/app directory, and then create a file named feedback/route.js inside the api directory. This structure will automatically create an API route at /api/feedback.

Add the following code to src/app/api/feedback/route.js:

import { NextResponse } from 'next/server';

export async function POST(request) {
  const data = await request.json();

  // In a real application, you would save this data to a database or send it to an analytics service.
  console.log('Received feedback:', data);

  // For this example, we'll just return a success response.
  return NextResponse.json({ message: 'Feedback received successfully!' }, { status: 200 });
}

Let’s break down the API route code:

  • Import NextResponse: This allows us to return HTTP responses.
  • POST Function: This function handles POST requests to the /api/feedback endpoint.
  • Request Handling: It parses the request body as JSON using request.json().
  • Data Processing (Placeholder): The code includes a placeholder for saving the feedback data. In a real-world scenario, you would integrate with a database (e.g., MongoDB, PostgreSQL) or a third-party service (e.g., an analytics platform).
  • Response: It returns a JSON response indicating success.

Now, when you submit the feedback form, the data will be logged to the console in your terminal where the Next.js development server is running. You should also see the success alert message in your browser.

Adding Real-Time Validation and Enhanced UI

The current form has basic validation. Let’s make it more robust. We’ll enhance the UI and add more features.

First, update the FeedbackForm.js component. We’ll add a rating system and improve the visual feedback for form submission.

import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import { FaComment, FaStar } from 'react-icons/fa';
import axios from 'axios';

const FeedbackForm = () => {
  const { register, handleSubmit, formState: { errors }, reset } = useForm();
  const [rating, setRating] = useState(0);
  const [hoverRating, setHoverRating] = useState(0);
  const [isSubmitting, setIsSubmitting] = useState(false);

  const onSubmit = async (data) => {
    setIsSubmitting(true);
    try {
      await axios.post('/api/feedback', { ...data, rating });
      alert('Thank you for your feedback!');
      reset();
      setRating(0);
    } catch (error) {
      console.error('Error submitting feedback:', error);
      alert('An error occurred. Please try again.');
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleStarClick = (selectedRating) => {
    setRating(selectedRating);
  };

  const handleStarHover = (hoveredRating) => {
    setHoverRating(hoveredRating);
  };

  const handleStarLeave = () => {
    setHoverRating(0);
  };

  const renderStars = () => {
    const stars = [];
    for (let i = 1; i <= 5; i++) {
      stars.push(
        <FaStar
          key={i}
          className="star"
          onClick={() => handleStarClick(i)}
          onMouseEnter={() => handleStarHover(i)}
          onMouseLeave={handleStarLeave}
          style={{ color: i <= (hoverRating || rating) ? '#ffc107' : '#ddd' }}
        />
      );
    }
    return <div className="stars">{stars}</div>;
  };

  return (
    <div className="feedback-form">
      <h2>We Value Your Feedback</h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div className="form-group">
          <label htmlFor="rating">Rating:</label>
          {renderStars()}
        </div>
        <div className="form-group">
          <label htmlFor="comment">Comments:</label>
          <textarea
            id="comment"
            {...register("comment", { required: true })}
            placeholder="Share your thoughts..."
          />
          {errors.comment && <span className="error">Comment is required</span>}
        </div>
        <button type="submit" disabled={isSubmitting}>
          {isSubmitting ? 'Submitting...' : 'Submit Feedback'} <FaComment />
        </button>
      </form>
    </div>
  );
};

export default FeedbackForm;

Key changes in this version:

  • State Variables: We use useState to manage the selected rating (rating), the hover state for the stars (hoverRating), and the submission state (isSubmitting).
  • Star Rendering: The renderStars function creates the star elements dynamically.
  • Star Interaction: The handleStarClick, handleStarHover, and handleStarLeave functions handle user interactions with the stars. Hovering changes the star color, and clicking selects the rating.
  • Dynamic Star Styling: The star color changes based on the hover state or the selected rating.
  • Submission State: The submit button is disabled while the form is submitting, and the text changes to indicate the submission in progress.
  • Data Submission: The rating is now submitted along with the comment.

Update the CSS in src/app/globals.css to include the active star color and the submission button state:


/* ... (Existing styles) ... */

.star.active {
  color: #ffc107;
}

button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

Now, your feedback widget will have a functional rating system and a more user-friendly interface.

Error Handling and User Feedback

Robust error handling is critical for providing a good user experience. Let’s improve the error handling in our API route and provide more informative feedback to the user.

Update the src/app/api/feedback/route.js file to handle potential errors during data processing:

import { NextResponse } from 'next/server';

export async function POST(request) {
  try {
    const data = await request.json();

    // Simulate an error (remove for production)
    // if (Math.random() < 0.2) {
    //   throw new Error('Simulated database error');
    // }

    // In a real application, you would save this data to a database or send it to an analytics service.
    console.log('Received feedback:', data);

    return NextResponse.json({ message: 'Feedback received successfully!' }, { status: 200 });
  } catch (error) {
    console.error('API Error:', error);
    return NextResponse.json({ message: 'Failed to submit feedback. Please try again later.' }, { status: 500 });
  }
}

Key changes:

  • Try-Catch Block: The code is wrapped in a try...catch block to handle potential errors during data processing.
  • Error Logging: If an error occurs, it’s logged to the server console.
  • Error Response: A 500 status code is returned with an error message to inform the user that the feedback submission failed.

In the FeedbackForm.js component, modify the `onSubmit` function to handle the error response from the API:


const onSubmit = async (data) => {
    setIsSubmitting(true);
    try {
      const response = await axios.post('/api/feedback', { ...data, rating });
      if (response.status === 200) {
        alert('Thank you for your feedback!');
        reset();
        setRating(0);
      } else {
        alert('An error occurred. Please try again.');
      }
    } catch (error) {
      console.error('Error submitting feedback:', error);
      alert('Failed to submit feedback. Please try again later.');
    } finally {
      setIsSubmitting(false);
    }
  };

Now, if there’s an error in the API, the user will receive a more informative error message. You can test the error handling by intentionally causing an error in your API route (e.g., uncomment the simulated error). Remember to remove the simulated error before deploying to production.

Deployment

Once you’ve tested your feedback widget thoroughly, it’s time to deploy it. Next.js applications can be deployed to various platforms, including Vercel, Netlify, and AWS. Vercel is the recommended platform for Next.js applications, as it provides a seamless deployment experience.

Here’s a general overview of the deployment process using Vercel:

  1. Create a Vercel Account: If you don’t already have one, create a free Vercel account at https://vercel.com/.
  2. Connect Your Git Repository: Connect your project’s Git repository (e.g., GitHub, GitLab, Bitbucket) to Vercel.
  3. Import Your Project: Import your project into Vercel. Vercel will automatically detect that it’s a Next.js application.
  4. Deploy: Vercel will build and deploy your application. You’ll receive a unique URL for your deployed application.
  5. Configure Environment Variables (if needed): If your application uses environment variables (e.g., API keys), you’ll need to configure them in Vercel’s settings.

For detailed instructions, refer to the Vercel documentation: https://vercel.com/docs.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • CORS Errors: If you’re encountering CORS (Cross-Origin Resource Sharing) errors when making API requests, you need to configure CORS on your API server. If you’re using a third-party API, consult its documentation for CORS configuration. For your own Next.js API routes, CORS is usually handled automatically, but you might need to adjust your server configuration if you’re using a custom backend. A common solution is to install and use a CORS middleware package.
  • Incorrect API Endpoint: Double-check that your API endpoint URL in the axios.post call is correct. Make sure the path matches the route you created in your api directory.
  • Missing Dependencies: Ensure you have installed all the necessary dependencies using npm install or yarn install.
  • Typographical Errors: Carefully review your code for any typos, especially in component names, variable names, and API endpoint URLs.
  • Server-Side vs. Client-Side Errors: Distinguish between errors that occur on the client-side (e.g., form validation) and server-side (e.g., database connection errors). Use the browser’s developer tools (Console tab) to identify the source of the error.
  • Environment Variables: If you’re using environment variables, make sure they are correctly configured in your deployment environment (e.g., Vercel) and that you are accessing them correctly in your code (e.g., using process.env.YOUR_VARIABLE).

Key Takeaways

  • User-Centric Design: Always prioritize user experience when designing your feedback widget. Make it easy for users to provide feedback.
  • Clear Communication: Provide clear and concise error messages to users. Let them know what went wrong and how to fix it.
  • Data Privacy: Be mindful of data privacy regulations (e.g., GDPR, CCPA). Clearly communicate how you’ll use the feedback data and obtain necessary consent.
  • Iterative Improvement: Continuously monitor and analyze the feedback you receive. Use the insights to improve your product or service.
  • Test Thoroughly: Test your feedback widget in various browsers and devices to ensure it works correctly for all users.

FAQ

Here are some frequently asked questions:

  1. Can I customize the feedback form’s design? Yes, you can fully customize the form’s design by modifying the CSS styles in src/app/globals.css or by using CSS-in-JS solutions.
  2. How do I store the feedback data? You can store the feedback data in a database (e.g., MongoDB, PostgreSQL), a third-party service (e.g., Firebase, SendGrid), or a spreadsheet. The choice depends on your requirements.
  3. Can I add more form fields to the feedback form? Yes, you can add more form fields (e.g., email address, name) to collect more information from users. Remember to update the form component, the API route, and the data storage mechanism accordingly.
  4. How can I prevent spam feedback? You can implement various techniques to prevent spam, such as CAPTCHA, rate limiting, and input validation.
  5. Is this widget accessible? The current widget has basic accessibility features. To improve accessibility, ensure proper use of semantic HTML, provide alt text for images, and use ARIA attributes where needed. Test with a screen reader to ensure proper navigation.

Building a feedback widget is more than just collecting data; it’s about fostering a dialogue with your audience, showing them that their opinions matter, and creating a cycle of continuous improvement. By implementing a well-designed feedback widget, you’re not just gathering data; you are creating a more engaging and user-centric experience for your users. The effort you invest in creating a streamlined and user-friendly method for gathering feedback will directly translate into a better understanding of your user base and ultimately, a better product. Furthermore, the ability to rapidly iterate and respond to user feedback is a powerful competitive advantage in today’s dynamic digital landscape. So, go forth, build your feedback widget, and start listening to what your users have to say; the insights you gain will be invaluable.