Next.js: Build an Interactive Simple Web-Based Quiz App

Written by

in

Quizzes are a fantastic way to engage users, test knowledge, and provide interactive experiences. From educational platforms to marketing websites, quizzes can serve various purposes. In this tutorial, we’ll dive into building a simple, yet functional, web-based quiz application using Next.js, a powerful React framework for building modern web applications. We’ll cover everything from setting up your project to implementing features like question display, answer validation, score tracking, and result presentation. This project is ideal for beginners and intermediate developers looking to expand their skills in Next.js and frontend development.

Why Build a Quiz App?

Creating a quiz app offers several benefits:

  • Interactive Engagement: Quizzes are inherently interactive, encouraging users to participate and stay engaged.
  • Knowledge Assessment: They provide a fun way to test knowledge and understanding of a specific topic.
  • Lead Generation: Quizzes can be used to collect user data and generate leads.
  • Educational Tool: They’re excellent for educational purposes, reinforcing learning in an engaging format.
  • Skill Development: Building a quiz app is a practical project for learning and practicing frontend development skills.

By the end of this tutorial, you’ll have a fully functional quiz app that you can customize and expand upon. This project will teach you about state management, component composition, event handling, and conditional rendering within the Next.js framework.

Prerequisites

Before we begin, make sure you have the following:

  • Node.js and npm (or yarn): Installed on your system.
  • Basic understanding of JavaScript and React: Familiarity with components, props, and state.
  • A code editor: Such as VS Code, Sublime Text, or Atom.

Setting Up Your Next.js Project

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

npx create-next-app quiz-app
cd quiz-app

This command creates a new Next.js project named “quiz-app” and navigates you into the project directory. Next.js sets up a basic project structure with essential files and configurations.

Project Structure Overview

Before we start coding, let’s briefly look at the project structure:

  • pages/: This directory contains your application’s pages. Each file in this directory represents a route. For example, pages/index.js will be the homepage (/).
  • components/: This directory is where you’ll store reusable React components.
  • styles/: This directory is for your CSS or other styling files.
  • public/: This directory holds static assets like images and fonts.
  • package.json: This file contains project dependencies and scripts.

Creating the Quiz Data

First, we need to define the quiz questions and answers. Create a new file named quizData.js in a directory called data inside the root directory of your project. This file will store an array of question objects. Each question object will contain the question text, an array of answer options, and the correct answer index.

// data/quizData.js
const quizData = [
  {
    question: "What is the capital of France?",
    options: ["Berlin", "Paris", "Madrid", "Rome"],
    correctAnswer: 1,
  },
  {
    question: "Which planet is known as the Red Planet?",
    options: ["Earth", "Mars", "Venus", "Jupiter"],
    correctAnswer: 1,
  },
  {
    question: "What is the highest mountain in the world?",
    options: ["K2", "Kangchenjunga", "Mount Everest", "Lhotse"],
    correctAnswer: 2,
  },
  {
    question: "What is the chemical symbol for water?",
    options: ["CO2", "O2", "H2O", "NaCl"],
    correctAnswer: 2,
  },
  {
    question: "Who painted the Mona Lisa?",
    options: ["Vincent van Gogh", "Leonardo da Vinci", "Pablo Picasso", "Michelangelo"],
    correctAnswer: 1,
  },
];

export default quizData;

This quizData.js file exports the array of questions which we will import and use in our app.

Building the Quiz Components

Now, let’s create the components for our quiz application. We’ll create three main components:

  • Question.js: Displays a single question and its answer options.
  • Quiz.js: Manages the quiz state, renders the questions, and handles user interactions.
  • Result.js: Displays the quiz results.

Question Component (components/Question.js)

Create a new file named Question.js inside the components directory. This component will render a single question and its answer options.

// components/Question.js
import React from 'react';

const Question = ({ question, options, correctAnswer, onAnswerSelect, selectedAnswer, isSubmitted }) => {
  return (
    <div>
      <p>{question}</p>
      {options.map((option, index) => (
        <button> onAnswerSelect(index)}
          disabled={isSubmitted}
          style={{
            backgroundColor: selectedAnswer === index ? (index === correctAnswer ? 'green' : 'red') : 'white',
            color: selectedAnswer === index ? 'white' : 'black',
            cursor: isSubmitted ? 'default' : 'pointer',
          }}
        >
          {option}
        </button>
      ))}
    </div>
  );
};

export default Question;

Here’s a breakdown of the Question component:

  • It receives question, options, correctAnswer, onAnswerSelect, selectedAnswer, and isSubmitted as props.
  • It displays the question text using a <p> tag.
  • It maps through the options array and renders a <button> for each option.
  • The onAnswerSelect function is called when a user clicks an answer, passing the index of the selected answer.
  • The buttons are disabled after submission.
  • The background color of the button changes based on whether the answer is selected and correct.

Quiz Component (components/Quiz.js)

Create a new file named Quiz.js inside the components directory. This component manages the quiz state, renders the questions, and handles user interactions.

// components/Quiz.js
import React, { useState } from 'react';
import Question from './Question';
import quizData from '../data/quizData';
import Result from './Result';

const Quiz = () => {
  const [currentQuestion, setCurrentQuestion] = useState(0);
  const [selectedAnswer, setSelectedAnswer] = useState(null);
  const [score, setScore] = useState(0);
  const [isSubmitted, setIsSubmitted] = useState(false);

  const handleAnswerSelect = (answerIndex) => {
    setSelectedAnswer(answerIndex);
  };

  const handleSubmit = () => {
    if (selectedAnswer === quizData[currentQuestion].correctAnswer) {
      setScore(score + 1);
    }
    setIsSubmitted(true);
  };

  const handleNextQuestion = () => {
    setSelectedAnswer(null);
    setIsSubmitted(false);
    setCurrentQuestion(currentQuestion + 1);
  };

  const handleRestart = () => {
    setCurrentQuestion(0);
    setSelectedAnswer(null);
    setScore(0);
    setIsSubmitted(false);
  };

  const isQuizOver = currentQuestion === quizData.length - 1 && isSubmitted;

  return (
    <div>
      {isQuizOver ? (
        
      ) : (
        <div>
          <p>Question {currentQuestion + 1} of {quizData.length}</p>
          
          {!isSubmitted ? (
            <button disabled="{selectedAnswer">Submit</button>
          ) : (
            currentQuestion < quizData.length - 1 ? (
              <button>Next Question</button>
            ) : null
          )}
        </div>
      )}
    </div>
  );
};

export default Quiz;

Here’s a breakdown of the Quiz component:

  • It imports useState, Question, quizData, and Result.
  • It uses the useState hook to manage the following state variables:
  • currentQuestion: The index of the current question.
  • selectedAnswer: The index of the selected answer.
  • score: The user’s score.
  • isSubmitted: A boolean to indicate whether the user has submitted the current question.
  • handleAnswerSelect: Updates the selectedAnswer state when an answer is chosen.
  • handleSubmit: Checks if the selected answer is correct, updates the score, and sets isSubmitted to true.
  • handleNextQuestion: Resets selectedAnswer and isSubmitted and increments currentQuestion.
  • handleRestart: Resets the quiz to its initial state.
  • It conditionally renders the Question component or the Result component based on whether the quiz is over.
  • It passes the necessary props to the Question component.

Result Component (components/Result.js)

Create a new file named Result.js inside the components directory. This component displays the quiz results.


// components/Result.js
import React from 'react';

const Result = ({ score, totalQuestions, onRestart }) => {
  const percentage = (score / totalQuestions) * 100;

  return (
    <div>
      <h2>Quiz Results</h2>
      <p>Your score: {score} out of {totalQuestions}</p>
      <p>Percentage: {percentage.toFixed(2)}%</p>
      <button>Restart Quiz</button>
    </div>
  );
};

export default Result;

Here’s a breakdown of the Result component:

  • It receives score, totalQuestions, and onRestart as props.
  • It calculates the percentage score.
  • It displays the user’s score, the total number of questions, and the percentage score.
  • It includes a button to restart the quiz.

Integrating Components in the Main Page

Now, let’s integrate these components into our main page (pages/index.js). Open pages/index.js and replace the default content with the following:


// pages/index.js
import React from 'react';
import Quiz from '../components/Quiz';

const Home = () => {
  return (
    <div>
      <h1>Simple Quiz App</h1>
      
    </div>
  );
};

export default Home;

Here’s what this code does:

  • It imports the Quiz component.
  • It renders the Quiz component inside a <div>.

Adding Styles (Optional)

To make the quiz app look more appealing, you can add some basic styles. For example, create a file named styles/Quiz.module.css and add the following CSS:


/* styles/Quiz.module.css */
.question {
  margin-bottom: 20px;
}

.options {
  display: flex;
  flex-direction: column;
}

.options button {
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f0f0f0;
  cursor: pointer;
}

.options button:hover {
  background-color: #e0e0e0;
}

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

.submitButton:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

Then, import and use these styles in your Quiz.js and Question.js components. For instance, in Quiz.js, you’d import the styles like this:

import styles from '../styles/Quiz.module.css';

And apply them to your elements:


<div className={styles.question}>...
<button className={styles.submitButton} ...>

Running Your Application

Save all your files. Open your terminal, navigate to your project directory (quiz-app), and run the following command to start the development server:

npm run dev
# or
yarn dev

This will start the Next.js development server. Open your web browser and go to http://localhost:3000 to see your quiz app in action.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them:

  • Incorrect Answer Index: Make sure the correctAnswer index in your quizData.js matches the correct answer’s position in the options array (0-based indexing).
  • State Not Updating: If the UI doesn’t update when you expect it to, double-check that you’re correctly updating the state variables using the setter functions from the useState hook (e.g., setCurrentQuestion(), setSelectedAnswer(), setScore()).
  • Button Disabling Issues: Ensure that your submit and next question buttons are correctly disabled or enabled based on the current state (e.g., whether an answer is selected, whether the question has been submitted).
  • Incorrect Imports: Verify that you’re importing the components and data correctly, paying attention to file paths and capitalization.
  • Uncaught Errors: Use your browser’s developer tools (Console tab) to identify and troubleshoot any errors. Next.js provides helpful error messages to guide you.

Enhancements and Next Steps

Here are some ways to enhance your quiz app:

  • Add More Question Types: Implement support for multiple-choice questions, true/false questions, and fill-in-the-blank questions.
  • Improve Styling: Use CSS frameworks like Tailwind CSS, Bootstrap, or Material UI for more advanced styling.
  • Implement Timer: Add a timer to each question to make the quiz more challenging.
  • Add User Authentication: Allow users to create accounts and save their quiz scores.
  • Fetch Questions from an API: Instead of hardcoding the quiz data, fetch questions from an API to make the quiz dynamic and easier to update.
  • Add a Progress Bar: Display a progress bar to show the user’s progress through the quiz.
  • Implement a Leaderboard: Create a leaderboard to display the top scores.
  • Make it Responsive: Ensure the quiz app works well on different screen sizes.

Summary / Key Takeaways

In this tutorial, we’ve successfully built a simple quiz application using Next.js. We’ve covered the basics of setting up a Next.js project, creating components, managing state, handling user interactions, and displaying results. You’ve learned how to structure your application, use the useState hook, and conditionally render UI elements based on the application’s state. Building this quiz app provides a solid foundation for understanding the core concepts of Next.js and frontend development. You can now adapt and expand this project to create more complex and engaging web applications. Remember to experiment with different features, explore the Next.js documentation, and practice your coding skills to become a proficient web developer. The ability to create interactive and engaging web experiences is a highly valuable skill in today’s digital landscape, and with Next.js, you have a powerful tool at your disposal. This quiz app is a perfect starting point for your journey into the world of web development.

FAQ

Q: How do I add more questions to the quiz?
A: Simply add more objects to the quizData array in your data/quizData.js file. Make sure each object has a question, options, and correctAnswer property.

Q: How can I style the quiz app?
A: You can add CSS styles using CSS modules, styled-components, or any other styling method you prefer. Create a CSS file (e.g., styles/Quiz.module.css) and import it into your components. Apply styles to your HTML elements using the className attribute.

Q: How do I deploy the quiz app?
A: You can deploy your Next.js app to various platforms, such as Vercel (recommended, as it’s built by the Next.js team), Netlify, or AWS. Each platform has its own deployment instructions. Generally, you’ll need to push your code to a Git repository and connect it to the deployment platform.

Q: Can I use a database to store quiz questions?
A: Yes, you can. You can connect to a database (e.g., MongoDB, PostgreSQL) using a library like Prisma or Mongoose. Fetch the quiz questions from the database in your Next.js application, and use the data to render the quiz. This is a great way to make the quiz dynamic and easier to manage.

Remember that the key to mastering any new technology is practice. As you continue to build and experiment with Next.js, you will gain a deeper understanding of its capabilities and how to leverage them to create amazing web applications. Keep exploring, keep building, and never stop learning!