Step-by-Step Implementation
Step-by-Step Implementation (React Quiz App)
Welcome to the build-along module! In this section, you’ll build the complete Quiz App using React. We’ll walk through environment setup, file and folder structure, components, state management, data fetching, and styling.
By the end, you’ll have a fully working, styled quiz app that fetches trivia questions from an API and displays results.
Set Up the Environment
Step 1: Install Node.js
Go to nodejs.org and download the LTS version. Install it.
Step 2: Create the App Using Vite
In your terminal, run:
npm create vite@latest quiz-app -- --template react
cd quiz-app
npm install
This sets up a minimal React + Vite project.
Step 3: Start the Dev Server
npm run dev
Open the provided localhost URL (usually http://localhost:5173) to view your app.
Organize the Folder Structure
Inside your project’s src folder, organize files like this:
src/
src/
├── components/
│ ├── Quiz.jsx
│ ├── QuestionCard.jsx
│ ├── WelcomeScreen.jsx
│ ├── CategorySelector.jsx
├── utils/
│ └── shuffle.js
├── App.jsx
├── style.css
This separation helps with clarity and scalability.
Build the App Component by Component
Step 1: App.jsx — The Main Component
This component manages the flow: welcome screen → category select → quiz → results.
Paste this in src/App.jsx:
// src/App.jsx
import React, { useState } from "react";
import Quiz from "./components/Quiz";
import "./style.css";
const App = () => {
const [start, setStart] = useState(false);
const [category, setCategory] = useState(9);
const [selected, setSelected] = useState(false);
const categories = [
{ id: 9, name: "General Knowledge" },
{ id: 21, name: "Sports" },
{ id: 23, name: "History" },
{ id: 17, name: "Science & Nature" },
{ id: 22, name: "Geography" },
{ id: 18, name: "Computers" },
{ id: 24, name: "Politics" },
{ id: 25, name: "Art" }
];
const handleStart = () => {
setStart(true);
};
return (
<div className="app">
{!start ? (
<div className="start-screen">
<h1>🎯 Welcome To My Quiz</h1>
{!selected ? (
<>
<p>Select the domain you’d like to test your knowledge in 🔍</p>
<select
onChange={(e) => setCategory(e.target.value)}
className="dropdown"
>
{categories.map((cat) => (
<option key={cat.id} value={cat.id}>
{cat.name}
</option>
))}
</select>
<button onClick={() => setSelected(true)} className="btn">
Confirm Category
</button>
</>
) : (
<>
<p>Great! Now I’ll test you and I hope you win! 💪🎉</p>
<button onClick={handleStart} className="btn">
Start Quiz
</button>
</>
)}
</div>
) : (
<Quiz category={category} />
)}
{/* Footer */}
<footer className="footer">
Made by Jaishree Tomar for{" "}
<a href="https://www.guvi.in" target="_blank" rel="noopener noreferrer">
GUVI
</a>
</footer>
</div>
);
};
export default App;Step 2: Quiz.jsx — Main Quiz Logic
Responsible for fetching questions, showing them, and tracking score.
Paste this in src/components/Quiz.jsx:
import React, { useEffect, useState } from "react";
import QuestionCard from "./QuestionCard";
const Quiz = ({ category }) => {
const [questions, setQuestions] = useState([]);
const [currentQn, setCurrentQn] = useState(0);
const [score, setScore] = useState(0);
const [answers, setAnswers] = useState([]);
const [loading, setLoading] = useState(true);
const shuffle = (arr) => [...arr].sort(() => Math.random() - 0.5);
const decodeHtml = (html) => {
const txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
};
useEffect(() => {
const fetchQuestions = async () => {
const url = `https://opentdb.com/api.php?amount=10&category=${category}&type=multiple`;
const res = await fetch(url);
const data = await res.json();
const formatted = data.results.map((q) => ({
...q,
question: decodeHtml(q.question),
correct_answer: decodeHtml(q.correct_answer),
options: shuffle([
...q.incorrect_answers.map(decodeHtml),
decodeHtml(q.correct_answer),
]),
}));
setQuestions(formatted);
setLoading(false);
};
fetchQuestions();
}, [category]);
const handleAnswer = (answer) => {
const correct = questions[currentQn].correct_answer;
if (answer === correct) setScore(score + 1);
setAnswers([
...answers,
{
question: questions[currentQn].question,
selected: answer,
correct,
isCorrect: answer === correct,
},
]);
setTimeout(() => {
if (currentQn + 1 < questions.length) {
setCurrentQn(currentQn + 1);
}
}, 300);
};
if (loading) return <h2 className="loading">⌛ Loading questions...</h2>;
if (answers.length === questions.length) {
return (
<div className="score-card">
<h2>🎉 Your Final Score: {score} / {questions.length}</h2>
<table className="results-table">
<thead>
<tr>
<th>#</th>
<th>Question</th>
<th>Your Answer</th>
<th>Correct Answer</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{answers.map((ans, idx) => (
<tr key={idx} className={ans.isCorrect ? "correct-row" : "wrong-row"}>
<td>{idx + 1}</td>
<td>{ans.question}</td>
<td>{ans.selected}</td>
<td>{ans.correct}</td>
<td>{ans.isCorrect ? "✅" : "❌"}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
return (
<QuestionCard
question={questions[currentQn].question}
options={questions[currentQn].options}
currentIndex={currentQn}
total={questions.length}
onAnswer={handleAnswer}
/>
);
};
export default Quiz;Step 3: QuestionCard.jsx — Display Each Question
Paste this in src/components/QuestionCard.jsx:
import React from "react";
const alphabets = ["A", "B", "C", "D"];
const QuestionCard = ({ question, options, currentIndex, total, onAnswer }) => {
return (
<div className="question-card">
<h2>❓ Question {currentIndex + 1} of {total}</h2>
<p className="question-text">{question}</p>
<div className="options">
{options.map((opt, idx) => (
<button
key={idx}
className="option-btn"
onClick={() => onAnswer(opt)}
>
<strong>{alphabets[idx]}.</strong> {opt}
</button>
))}
</div>
</div>
);
};
export default QuestionCard;Style the App
Paste this in src/style.css:
body {
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #121212;
color: #ffffff;
}
.app {
text-align: center;
padding: 20px;
}
.start-screen {
margin-top: 60px;
}
h1 {
font-size: 2.5rem;
margin-bottom: 20px;
color: #00ffff;
}
p {
font-size: 1.2rem;
margin: 10px 0 20px;
}
.dropdown {
padding: 10px;
font-size: 1rem;
border-radius: 6px;
border: none;
margin-bottom: 20px;
}
.btn {
padding: 10px 25px;
font-size: 1rem;
border: none;
border-radius: 8px;
background-color: #00bfff;
color: white;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 10px;
}
.btn:hover {
background-color: #009acd;
}
.loading {
font-size: 1.5rem;
color: #ffcc00;
margin-top: 50px;
}
.question-card {
margin-top: 50px;
background-color: #1e1e1e;
padding: 30px;
border-radius: 12px;
max-width: 800px;
margin-left: auto;
margin-right: auto;
box-shadow: 0 0 15px rgba(0, 191, 255, 0.2);
}
.question-text {
font-size: 1.25rem;
margin-bottom: 30px;
}
.options {
display: flex;
flex-direction: column;
gap: 15px;
}
.option-btn {
padding: 12px 20px;
border: none;
border-radius: 8px;
background-color: #2e2e2e;
color: white;
font-size: 1rem;
text-align: left;
transition: background-color 0.3s ease, transform 0.2s;
cursor: pointer;
}
.option-btn:hover {
background-color: #00bfff;
transform: translateY(-2px);
}
.option-btn.correct {
background-color: #28a745 !important;
color: white;
}
.option-btn.incorrect {
background-color: #dc3545 !important;
color: white;
}
.score-card {
margin-top: 40px;
padding: 20px;
max-width: 800px;
margin-left: auto;
margin-right: auto;
background-color: #1e1e1e;
border-radius: 12px;
box-shadow: 0 0 15px rgba(255, 255, 255, 0.05);
}
.score-card h2 {
color: #00ffff;
margin-bottom: 20px;
}
.answer-list {
margin-top: 20px;
}
.answer-block {
margin-bottom: 20px;
background-color: #2c2c2c;
padding: 15px;
border-radius: 8px;
text-align: left;
}
.answer-block.correct {
border-left: 6px solid #28a745;
}
.answer-block.wrong {
border-left: 6px solid #dc3545;
}
.answer-block h4 {
margin: 0 0 10px;
color: #ffffff;
}
.answer-block p {
margin: 4px 0;
color: #cccccc;
}
/* Footer Section */
.footer {
margin-top: 40px;
text-align: center;
color: #cccccc;
font-size: 0.9rem;
padding-bottom: 20px;
}
.footer a {
color: #00bfff;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}Utilities and Helpers
In src/utils/shuffle.js:
const shuffle = (array) => {
const newArr = [...array];
for (let i = newArr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[newArr[i], newArr[j]] = [newArr[j], newArr[i]];
}
return newArr;
};
export default shuffle;This ensures answers are randomized.
Run and Test the App Locally
In the terminal:
npm start
This will open your app at http://localhost:3000.
Try selecting a category, answering questions, and checking your score at the end.


