🧠 Build a 3-Screen React Quiz App with JSON & Fetch
This tutorial shows how to build a React Quiz App with 3 screens and data coming from JSON files hosted on GitHub Pages. We'll use fetch to load data dynamically and React Router to handle screen transitions.
Code on Github
Live on Github Pages
🧱 App Structure
- Screen 1: List of Subjects
- Screen 2: List of Quizzes in Selected Subject
- Screen 3: Quiz Viewer
📁 JSON Data Example
subjects.json
[
{
"subjectNo": 1,
"subjectName": "JavaScript",
"quizListUrl": "https://yourusername.github.io/data/js-quizzes.json"
},
{
"subjectNo": 2,
"subjectName": "Python",
"quizListUrl": "https://yourusername.github.io/data/python-quizzes.json"
}
]
js-quizzes.json
[
{
"quizNo": 1,
"quizName": "JS Basics",
"quizUrl": "https://yourusername.github.io/data/js-basics-quiz.json"
}
]
js-basics-quiz.json
[
{
"question": "What does JS stand for?",
"options": ["JavaScript", "JustScript", "JScript", "Java"],
"answer": "JavaScript"
}
]
---
🚀 Project Setup
npx create-react-app react-quiz-app
cd react-quiz-app
npm install react-router-dom
---
📂 File: App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import SubjectList from './SubjectList';
import QuizList from './QuizList';
import QuizScreen from './QuizScreen';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<SubjectList />} />
<Route path="/quizzes" element={<QuizList />} />
<Route path="/quiz" element={<QuizScreen />} />
</Routes>
</Router>
);
}
export default App;
---
📂 File: SubjectList.js
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
function SubjectList() {
const [subjects, setSubjects] = useState([]);
const navigate = useNavigate();
useEffect(() => {
fetch('https://yourusername.github.io/data/subjects.json')
.then(res => res.json())
.then(data => setSubjects(data));
}, []);
const handleClick = (quizListUrl) => {
localStorage.setItem('quizListUrl', quizListUrl);
navigate('/quizzes');
};
return (
<div>
<h2>Select Subject</h2>
{subjects.map(sub => (
<button key={sub.subjectNo} onClick={() => handleClick(sub.quizListUrl)}>
{sub.subjectName}
</button>
))}
</div>
);
}
export default SubjectList;
---
📂 File: QuizList.js
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
function QuizList() {
const [quizzes, setQuizzes] = useState([]);
const navigate = useNavigate();
useEffect(() => {
const url = localStorage.getItem('quizListUrl');
fetch(url)
.then(res => res.json())
.then(data => setQuizzes(data));
}, []);
const handleQuizClick = (quizUrl) => {
localStorage.setItem('quizUrl', quizUrl);
navigate('/quiz');
};
return (
<div>
<h2>Select Quiz</h2>
{quizzes.map(q => (
<button key={q.quizNo} onClick={() => handleQuizClick(q.quizUrl)}>
{q.quizName}
</button>
))}
</div>
);
}
export default QuizList;
---
📂 File: QuizScreen.js
import { useEffect, useState } from 'react';
function QuizScreen() {
const [questions, setQuestions] = useState([]);
const [index, setIndex] = useState(0);
const [score, setScore] = useState(0);
const [showResult, setShowResult] = useState(false);
useEffect(() => {
const url = localStorage.getItem('quizUrl');
fetch(url)
.then(res => res.json())
.then(data => setQuestions(data));
}, []);
const handleOptionClick = (option) => {
if (option === questions[index].answer) setScore(score + 1);
const next = index + 1;
if (next < questions.length) {
setIndex(next);
} else {
setShowResult(true);
}
};
if (!questions.length) return <p>Loading...</p>;
if (showResult) return <h2>Your Score: {score}/{questions.length}</h2>;
const q = questions[index];
return (
<div>
<h3>Q{index + 1}: {q.question}</h3>
{q.options.map((opt, i) => (
<button key={i} onClick={() => handleOptionClick(opt)}>{opt}</button>
))}
</div>
);
}
export default QuizScreen;
---
0 Comments