Build a Quiz App with React and TypeScript
Last Updated :
06 Feb, 2025
Improve
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

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.
//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;
//indes.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<App />
);
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
