Building an Interactive Quiz App with ReactJS

Building an Interactive Quiz App with ReactJS

Creating a dynamic and engaging quiz application is an excellent project to enhance your ReactJS skills. In this tutorial, we’ll build an interactive quiz app using ReactJS that dynamically manages questions, tracks scores, and displays results.

Project Overview

Our quiz app will:

  • Display multiple-choice questions.
  • Track the user’s score.
  • Show feedback on selected answers.
  • Display results at the end with an option to restart.

Follow this video for complete guidance :

Getting Started

Ensure you have Node.js and npm installed. Create a new React app:

npx create-react-app quiz-app
cd quiz-app
npm start

Building the Quiz Component

Below is the complete code for our quiz component.

import React, { useState } from 'react';

function Quiz() {
  const questions = [
    { question: 'What is React?', options: ['Library', 'Framework', 'Language', 'Tool'], answer: 'Library' },
    { question: 'Who developed React?', options: ['Google', 'Facebook', 'Microsoft', 'Apple'], answer: 'Facebook' },
    { question: 'Which hook manages state?', options: ['useEffect', 'useState', 'useContext', 'useReducer'], answer: 'useState' }
  ];

  const [currentQuestion, setCurrentQuestion] = useState(0);
  const [score, setScore] = useState(0);
  const [showResult, setShowResult] = useState(false);
  const [selectedAnswer, setSelectedAnswer] = useState(null);
  const [isAnswered, setIsAnswered] = useState(false);

  const handleAnswer = (option) => {
    if (!isAnswered) {
      setSelectedAnswer(option);
      setIsAnswered(true);
      
      if (option === questions[currentQuestion].answer) {
        setScore(score + 1);
      }

      setTimeout(() => {
        const nextQuestion = currentQuestion + 1;
        if (nextQuestion < questions.length) {
          setCurrentQuestion(nextQuestion);
          setSelectedAnswer(null);
          setIsAnswered(false);
        } else {
          setShowResult(true);
        }
      }, 1000);
    }
  };

  const resetQuiz = () => {
    setCurrentQuestion(0);
    setScore(0);
    setShowResult(false);
    setSelectedAnswer(null);
    setIsAnswered(false);
  };

  const getButtonClass = (option) => {
    const baseClass = 'btn w-100 text-start p-3 mb-3 position-relative';
    if (!isAnswered) return `${baseClass} btn-outline-primary`;
    if (option === questions[currentQuestion].answer) return `${baseClass} btn-success`;
    if (option === selectedAnswer) return `${baseClass} btn-danger`;
    return `${baseClass} btn-outline-secondary`;
  };

  return (
    <div className="min-vh-100 d-flex align-items-center justify-content-center bg-gradient" 
         style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
      <div className="container">
        <div className="row justify-content-center">
          <div className="col-12 col-md-8 col-lg-6">
            <div className="card shadow-lg">
              <div className="card-body p-4">
                <h1 className="text-center mb-4">
                  <i className="fas fa-brain text-primary me-2"></i>
                  Interactive Quiz
                </h1>

                {showResult ? (
                  <div className="text-center">
                    <i className="fas fa-trophy text-warning mb-4" style={{ fontSize: '4rem' }}></i>
                    <h3 className="mb-3">Quiz Complete!</h3>
                    <div className="display-4 text-primary mb-3">
                      {score} / {questions.length}
                    </div>
                    <p className="text-muted mb-4">
                      {score === questions.length 
                        ? 'Perfect score! Excellent work! ๐ŸŽ‰' 
                        : 'Good effort! Keep practicing! ๐Ÿ’ช'}
                    </p>
                    <button 
                      className="btn btn-outline-primary btn-lg"
                      onClick={resetQuiz}
                    >
                      <i className="fas fa-redo-alt me-2"></i>
                      Try Again
                    </button>
                  </div>
                ) : (
                  <div>
                    <div className="mb-4">
                      <div className="progress mb-2" style={{ height: '8px' }}>
                        <div 
                          className="progress-bar bg-primary" 
                          style={{ width: `${((currentQuestion + 1) / questions.length) * 100}%` }}
                        ></div>
                      </div>
                      <small className="text-muted text-center d-block">
                        Question {currentQuestion + 1} of {questions.length}
                      </small>
                    </div>

                    <h5 className="mb-4">{questions[currentQuestion].question}</h5>

                    <div className="options">
                      {questions[currentQuestion].options.map((option) => (
                        <button
                          key={option}
                          onClick={() => handleAnswer(option)}
                          className={getButtonClass(option)}
                          disabled={isAnswered}
                        >
                          {option}
                          {isAnswered && option === questions[currentQuestion].answer && (
                            <i className="fas fa-check position-absolute end-0 me-3 top-50 translate-middle-y"></i>
                          )}
                        </button>
                      ))}
                    </div>
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>
      </div>

      <style>
        {`
          body{
           background:#f2f2f2;
          }
          .btn-outline-primary:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
          }

          .btn {
            transition: all 0.3s ease;
          }

          .card {
            border-radius: 1rem;
            border: none;
          }

          .progress {
            border-radius: 1rem;
            background-color: #e9ecef;
          }

          .progress-bar {
            border-radius: 1rem;
          }
        `}
      </style>
    </div>
  );
}

export default Quiz;

Explanation of the Code

State Management:

  • currentQuestion: Tracks the current question index.
  • score: Tracks correct answers.
  • showResult: Toggles the result screen.
  • selectedAnswer: Stores the user’s selected option.
  • isAnswered: Prevents multiple clicks on the same question.

Answer Handling:

  • Correct answers increase the score.
  • Progresses to the next question after 1 second.

Dynamic Styling:

  • Buttons change color based on the correctness of the selected answer.

Running the App

Start your development server:

npm start

Visit http://localhost:3000 to see your interactive quiz app.

This project demonstrates React’s core concepts, including state management, dynamic rendering, and event handling. You can extend the app by adding features like a timer, category filters, or a backend API for dynamic questions.

Happy coding! ๐Ÿš€

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *