Open In App

Build a Quiz App with React and TypeScript

Last Updated : 06 Feb, 2025
Summarize
Comments
Improve
Suggest changes
Like Article
Like
Share
Report
News Follow

We will build a Quiz App using React and TypeScript by managing questions, user selections, and a countdown timer with useState and useEffect. The app will track the score, display results, and allow users to restart, ensuring a smooth and interactive quiz experience.

What We’re Going to Create

  • State Management: Use useState for questions, score, and timer.
  • Countdown Timer: Implement useEffect for dynamic timing.
  • User Interaction: Handle answer selection and navigation.
  • Score Tracking: Update scores dynamically.
  • Styling: Apply inline styles with JavaScript objects.
  • Optimization: Ensure efficient re-renders in React.

Project Preview

Screenshot-2025-02-04-100912

Build a Quiz App with React and TypeScript

Creating The Application

Follow the article to create and setup React Project with TypeScript – Use TypeScript with React

This React Quiz App is built using TypeScript and uses inline CSS styling for a clean UI. It features a countdown timer, score tracking, and a restart option.

JavaScript
//App.tsx

import React, { useState, useEffect } from "react";
interface Question {
    question: string;
    options: string[];
    answer: string;
}

const questions: Question[] = [
    {
        question: "What is the capital of France?",
        options: ["Berlin", "Madrid", "Paris", "Lisbon"],
        answer: "Paris",
    },
    {
        question: "Which language is used for web development?",
        options: ["Python", "Java", "C++", "JavaScript"],
        answer: "JavaScript",
    },
    {
        question: "Who developed the theory of relativity?",
        options: ["Newton", "Einstein", "Galileo", "Tesla"],
        answer: "Einstein",
    },
];

const QuizApp: React.FC = () => {
    const [currentQuestion, setCurrentQuestion] = useState < number > (0);
    const [score, setScore] = useState < number > (0);
    const [showResult, setShowResult] = useState < boolean > (false);
    const [timeLeft, setTimeLeft] = useState < number > (10);

    useEffect(() => {
        if (timeLeft === 0) {
            handleNextQuestion();
        }
        const timer = setInterval(() => {
            setTimeLeft((prev) => (prev > 0 ? prev - 1 : 0));
        }, 1000);
        return () => clearInterval(timer);
    }, [timeLeft]);

    const handleAnswer = (option: string) => {
        if (option === questions[currentQuestion].answer) {
            setScore(score + 1);
        }
        handleNextQuestion();
    };

    const handleNextQuestion = () => {
        const nextQuestion = currentQuestion + 1;
        if (nextQuestion < questions.length) {
            setCurrentQuestion(nextQuestion);
            setTimeLeft(10);
        } else {
            setShowResult(true);
        }
    };

    // Inline Style Objects
    const styles = {
        container: {
            display: "flex",
            flexDirection: "column" as "column",
            alignItems: "center",
            justifyContent: "center",
            minHeight: "100vh",
            background: "linear-gradient(to bottom right, #6b46c1, #4c51bf)",
            padding: "20px",
            color: "white",
        },
        quizBox: {
            background: "white",
            color: "#333",
            padding: "20px",
            borderRadius: "20px",
            boxShadow: "0px 10px 20px rgba(0, 0, 0, 0.3)",
            width: "400px",
            textAlign: "center" as "center",
        },
        questionTitle: {
            fontSize: "1.5rem",
            fontWeight: "bold",
            color: "#4c51bf",
            marginBottom: "15px",
        },
        timer: {
            fontSize: "1.2rem",
            fontWeight: "bold",
            color: "red",
            marginBottom: "15px",
        },
        optionsContainer: {
            display: "flex",
            flexDirection: "column" as "column",
            gap: "10px",
        },
        optionButton: {
            padding: "12px 16px",
            fontSize: "1rem",
            background: "#4c51bf",
            color: "white",
            border: "none",
            borderRadius: "10px",
            cursor: "pointer",
            transition: "0.3s",
        },
        optionButtonHover: {
            background: "#3730a3",
        },
        resultContainer: {
            textAlign: "center" as "center",
        },
        restartButton: {
            marginTop: "20px",
            padding: "12px 16px",
            fontSize: "1rem",
            background: "#4c51bf",
            color: "white",
            border: "none",
            borderRadius: "10px",
            cursor: "pointer",
            transition: "0.3s",
        },
    };

    return (
        <div style={styles.container}>
            <div style={styles.quizBox}>
                {showResult ? (
                    <div style={styles.resultContainer}>
                        <h2 style={styles.questionTitle}>Quiz Completed!</h2>
                        <p className="text-xl mt-2">
                            Your Score: {score} / {questions.length}
                        </p>
                        <button
                            style={styles.restartButton}
                            onClick={() => {
                                setCurrentQuestion(0);
                                setScore(0);
                                setShowResult(false);
                                setTimeLeft(10);
                            }}
                        >
                            Restart Quiz
                        </button>
                    </div>
                ) : (
                    <div style={{ textAlign: "center" }}>
                        <h2 style={styles.questionTitle}>{questions[currentQuestion].question}</h2>
                        <p style={styles.timer}>Time Left: {timeLeft}s</p>
                        <div style={styles.optionsContainer}>
                            {questions[currentQuestion].options.map((option) => (
                                <button
                                    key={option}
                                    style={styles.optionButton}
                                    onMouseOver={(e) => (e.currentTarget.style.background = 
                                        styles.optionButtonHover.background)}
                                    onMouseOut={(e) => (e.currentTarget.style.background = 
                                        styles.optionButton.background)}
                                    onClick={() => handleAnswer(option)}
                                >
                                    {option}
                                </button>
                            ))}
                        </div>
                    </div>
                )}
            </div>
        </div>
    );
};
export default QuizApp;
JavaScript

In this example

  • State Management: Uses useState to track questions, score, results, and timer for smooth UI updates.
  • Countdown Timer: Implements useEffect to decrease time and auto-move to the next question when time runs out.
  • Answer Handling: Validates selected answers, updates the score, and proceeds to the next question.
  • Quiz Flow: Manages question navigation and displays the final score after the last question.
  • Inline Styling: Defines styles with JavaScript objects, avoiding external CSS.
  • Interactive UI: Changes button colors on hover for better user experience.
  • Restart Quiz: Resets the quiz state, allowing users to replay without refreshing.

To start he application run the following command

npm run start

Output


quiz


Next Article

Similar Reads

three90RightbarBannerImg