Build a Simple Next.js Interactive Web-Based Survey App

Written by

in

Surveys are everywhere. From gathering customer feedback to understanding public opinion, they’re a crucial tool for collecting data and making informed decisions. Wouldn’t it be great to build your own, tailored to your specific needs? In this tutorial, we’ll dive into creating a simple yet functional web-based survey application using Next.js, a powerful React framework, perfect for beginners and intermediate developers alike. We’ll cover everything from setting up the project to deploying it, ensuring you have a solid understanding of the core concepts along the way.

Why Build a Survey App with Next.js?

Next.js offers several advantages for this project:

  • Server-Side Rendering (SSR) and Static Site Generation (SSG): Improve SEO and initial load times.
  • Routing: Easy to manage different survey sections and pages.
  • API Routes: Simple way to handle form submissions and data storage.
  • Developer Experience: Excellent tooling, including hot-reloading and built-in CSS support.

By using Next.js, you’re not just building a survey app; you’re learning modern web development best practices.

Project Setup: Getting Started

Let’s begin by setting up our Next.js project. Open your terminal and run the following commands:

npx create-next-app survey-app
cd survey-app
npm install react-hook-form

This creates a new Next.js project named “survey-app”, navigates into the project directory, and installs react-hook-form, a library that simplifies form management in React. We’ll use this library to handle our survey form’s logic.

Designing the Survey Structure

Before we start coding, let’s plan the structure of our survey. We’ll keep it simple, with a few question types:

  • Multiple Choice: Users select one option from a list.
  • Text Input: Users provide a short text response.
  • Rating Scale: Users rate on a scale (e.g., 1-5 stars).

We’ll represent each question as an object with properties like question text, question type, and options (for multiple-choice questions).

Building the Survey Components

1. The Question Component

Let’s create a reusable component for each question. Create a file named components/Question.js and add the following code:

import React from 'react';
import { useFormContext } from 'react-hook-form';

function Question({ question, index }) {
  const { register, formState: { errors } } = useFormContext();

  switch (question.type) {
    case 'multipleChoice':
      return (
        <div>
          <label htmlFor={`question-${index}`}>{question.text}</label>
          {question.options.map((option, optionIndex) => (
            <div key={optionIndex}>
              <input
                type="radio"
                id={`question-${index}-${optionIndex}`}
                value={option}
                {...register(`question-${index}`)}
              /
              >
              <label htmlFor={`question-${index}-${optionIndex}`}>{option}</label>
            </div>
          ))}
          {errors[`question-${index}`] && <p>This field is required</p>}
        </div>
      );
    case 'textInput':
      return (
        <div>
          <label htmlFor={`question-${index}`}>{question.text}</label>
          <input
            type="text"
            id={`question-${index}`}
            {...register(`question-${index}`, { required: true })}
          /
          >
          {errors[`question-${index}`] && <p>This field is required</p>}
        </div>
      );
    case 'ratingScale':
      return (
        <div>
          <label htmlFor={`question-${index}`}>{question.text}</label>
          <select id={`question-${index}`}
            {...register(`question-${index}`, { required: true })}
          >
            <option value="">Select a rating</option>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
            <option value="4">4</option>
            <option value="5">5</option>
          </select>
          {errors[`question-${index}`] && <p>This field is required</p>}
        </div>
      );
    default:
      return <p>Unsupported question type</p>;
  }
}

export default Question;

This component takes a question object and an index prop. It uses a switch statement to render different input types based on the question.type. We use react-hook-form‘s register function to handle the form fields and errors to display validation messages. The useFormContext hook is used to access the form context provided by react-hook-form.

2. The Survey Form Component

Now, let’s create the main survey form. Create a file named components/SurveyForm.js:

import React from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import Question from './Question';

function SurveyForm({ questions, onSubmit }) {
  const methods = useForm();
  const { handleSubmit } = methods;

  return (
    <FormProvider {...methods}>
      <form onSubmit={handleSubmit(onSubmit)}>
        {questions.map((question, index) => (
          <Question key={index} question={question} index={index} />
        ))}
        <button type="submit">Submit Survey</button>
      </form>
    </FormProvider>
  );
}

export default SurveyForm;

This component imports the Question component and uses react-hook-form‘s useForm hook to manage the form state and submission. It iterates through the questions array, rendering a Question component for each one. The onSubmit prop is a function that will be called when the form is submitted.

Implementing the Survey Logic

1. Define the Questions

Let’s define our survey questions. In your pages/index.js file, add the following code inside the Home component:

const questions = [
  {
    text: "How satisfied are you with our product?",
    type: "ratingScale",
  },
  {
    text: "What is your favorite feature?",
    type: "textInput",
  },
  {
    text: "Would you recommend us to a friend?",
    type: "multipleChoice",
    options: ["Yes", "No", "Maybe"],
  },
];

This array holds the structure for each question. You can easily add, remove, or modify questions here to customize your survey.

2. Render the Survey Form

Now, let’s render the SurveyForm component in pages/index.js:

import SurveyForm from '../components/SurveyForm';

function Home() {
  const questions = [
    {
      text: "How satisfied are you with our product?",
      type: "ratingScale",
    },
    {
      text: "What is your favorite feature?",
      type: "textInput",
    },
    {
      text: "Would you recommend us to a friend?",
      type: "multipleChoice",
      options: ["Yes", "No", "Maybe"],
    },
  ];

  const handleSubmit = (data) => {
    console.log(data); // Replace with your submission logic
    alert('Survey submitted!');
  };

  return (
    <div>
      <SurveyForm questions={questions} onSubmit={handleSubmit} />
    </div>
  );
}

export default Home;

Here, we import SurveyForm, pass our questions array, and define an onSubmit function. The handleSubmit function currently logs the form data to the console and displays an alert. You’ll replace this with your actual submission logic (e.g., sending data to an API).

Adding Styling

To make our survey app look presentable, let’s add some basic styling. Create a file named styles/globals.css (if it doesn’t already exist) and add the following CSS:

body {
  font-family: sans-serif;
  margin: 20px;
}

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

input[type="radio"], input[type="text"], select {
  margin-bottom: 10px;
  padding: 5px;
  border: 1px solid #ccc;
  border-radius: 4px;
  width: 100%; /* Or adjust as needed */
}

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

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

p {
  color: red;
  margin-top: -5px;
  margin-bottom: 10px;
}

This CSS provides basic styling for the form elements. You can customize this to fit your preferences. Next.js automatically imports globals.css, so you don’t need to import it explicitly in your components.

Handling Form Submissions (API Routes)

The next step is to handle the form submission. We’ll use Next.js API routes to create an endpoint for receiving the survey data. Create a file named pages/api/submit-survey.js:

export default async function handler(req, res) {
  if (req.method === 'POST') {
    try {
      // Process the data (e.g., save to a database)
      const surveyData = req.body;
      console.log('Received survey data:', surveyData);

      // Simulate saving to a database (replace with your actual logic)
      // await saveSurveyDataToDatabase(surveyData);

      res.status(200).json({ message: 'Survey submitted successfully!' });
    } catch (error) {
      console.error('Error submitting survey:', error);
      res.status(500).json({ message: 'Error submitting survey' });
    }
  } else {
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

This API route handles POST requests. It parses the request body (which contains the survey data), logs it to the console, and then sends a success response. You’ll replace the commented-out code with your database interaction logic (e.g., using a library like Prisma or Mongoose to connect to your database).

Important: You’ll need to install a library like body-parser or use the built-in req.body parsing in Next.js to access the request body. Next.js automatically parses the body for JSON requests, so you don’t need to install body-parser explicitly.

To use this API route, modify your handleSubmit function in pages/index.js:

const handleSubmit = async (data) => {
  try {
    const response = await fetch('/api/submit-survey', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    });

    if (response.ok) {
      alert('Survey submitted successfully!');
    } else {
      alert('There was an error submitting the survey.');
    }
  } catch (error) {
    console.error('Error submitting survey:', error);
    alert('There was an error submitting the survey.');
  }
};

This updated handleSubmit function sends a POST request to the /api/submit-survey endpoint with the survey data. It then handles the response, displaying a success or error message.

Common Mistakes and How to Fix Them

  • Incorrect Form Field Names: Make sure the names you use with register in your Question component match the keys in the data you’re submitting. Incorrect names will result in empty data being sent. Double-check your component’s register calls and your API route’s data parsing to make sure they align.
  • Missing Required Fields: If you’ve set required: true in your register options, the form won’t submit until all required fields are filled. Make sure you’ve included these validations where necessary and provide clear error messages to the user.
  • CORS Errors: If you’re making API calls to a different domain, you might encounter Cross-Origin Resource Sharing (CORS) errors. You’ll need to configure CORS on your API server to allow requests from your Next.js application’s origin. For local development, you might use a browser extension to disable CORS, but this is not recommended for production.
  • Uncaught Errors in API Routes: Always wrap your API route logic in a try...catch block to handle potential errors gracefully. Log errors to the console (server-side) and return appropriate error responses to the client.
  • Incorrect Content-Type Header: When sending data to your API route, make sure you set the Content-Type header to application/json. This tells the server that the request body is in JSON format. Without this header, the server might not be able to parse the data correctly.

Key Takeaways and Summary

In this tutorial, we’ve built a simple, interactive survey application using Next.js. We’ve covered the following key aspects:

  • Project setup and the advantages of using Next.js.
  • Designing the survey structure and creating reusable components.
  • Implementing different question types with react-hook-form.
  • Handling form submissions using Next.js API routes.
  • Adding basic styling.

You can extend this project by adding more question types, implementing more complex validation, connecting to a database to store the survey results, and adding features like progress indicators or conditional questions. This project provides a solid foundation for building more sophisticated survey applications.

FAQ

1. How do I deploy my Next.js survey app?

You can deploy your Next.js app to various platforms, including Vercel (recommended, as it’s built by the Next.js team), Netlify, or other hosting providers. For Vercel, simply push your code to a Git repository (like GitHub or GitLab), and Vercel will automatically deploy your app.

2. How can I store the survey data?

You can store the survey data in various ways, such as:

  • Databases: Use a database like PostgreSQL, MongoDB, or MySQL. You’ll need to install a database client library (e.g., Prisma, Mongoose) and configure your API routes to interact with the database.
  • Cloud Storage: Store the data in cloud storage services like AWS S3 or Google Cloud Storage.
  • Spreadsheets: Use services like Google Sheets API to save the data in a spreadsheet. This is a simpler option for smaller surveys.

3. How can I add more question types?

To add more question types, you’ll need to:

  • Extend the Question component to handle the new question type. Add a new case to the switch statement, rendering the appropriate input elements and using register to handle the form field.
  • Update the questions array in your pages/index.js or wherever you define your questions to include the new question type.
  • Consider adding validation rules specific to the new question type.

4. How do I add validation to my survey?

react-hook-form provides robust validation features. You can add validation rules to your register options in the Question component. For example:

{...register('email', { required: 'Email is required', pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,}$/i, message: 'Invalid email address' } })}

This example adds a required validation and an email pattern validation. react-hook-form also provides access to error messages through the errors object, allowing you to display validation messages to the user.

5. How can I improve the user experience?

Consider the following to improve UX:

  • Progress Indicators: Show users their progress through the survey.
  • Conditional Questions: Display questions based on previous answers.
  • Error Handling: Provide clear and helpful error messages.
  • Accessibility: Ensure your survey is accessible to users with disabilities. Use semantic HTML and ARIA attributes.
  • Mobile Responsiveness: Make sure the survey looks good on all devices.

By implementing these features, you can create a more engaging and user-friendly survey experience.

Building a survey application with Next.js opens up possibilities for gathering valuable insights. By following the steps outlined in this tutorial and experimenting with the different features, you can create powerful and interactive surveys tailored to your specific needs. Remember that the key to mastering any new technology is practice and exploration. So, take this project as a starting point, experiment with different features, and see what you can create!