On This Page
- # Understand The Logic Behind The Quiz App
- # Quiz Questions Javascript File
- # Quiz Question Structure Explanation
- # Step-by-Step Quiz App Development
- # Code Uptill now
- # Complete Component of Reactjs Quiz App
- # CSS
- # Apply What You've Learned
- # React Quiz App Template
- # Codepen Demo | Reactjs Quiz App
You might make a ReactJS quiz app if you're starting in web development or taking a basic-level course. One of any language's most fundamental projects is this one. You can learn so many concepts while building quiz app in Reactjs.
- How
useState
works in ReactJS while keep track ofactive questions
,selected answer
andresult
. - How to conditionally add classes to highlight the answer user has selected.
- Catching edge cases, like in case of last question we will change button title from Next to Finish and display the result to user. Add leading zero if question number is less than 10.
Understand The Logic Behind The Quiz App
To understand how quiz app works, we need to understand how following logic works.
- We have to track question number.
- Function runs on clicking next button.
- We have to keep track of all the answers selected by user, to calculate the score to show on result screen.
- Quiz question file, the same structure can be used in
APIs
.
Quiz Questions Javascript File
This is how we structured the quiz questions, the same format can be used for APIs
.
// Question Types// 1. MCQs | Multiple Choice | singleexport const quiz = {topic: 'Javascript',level: 'Beginner',totalQuestions: 4,perQuestionScore: 5,questions: [{question: 'Which function is used to serialize an object into a JSON string in Javascript?',choices: ['stringify()', 'parse()', 'convert()', 'None of the above'],type: 'MCQs',correctAnswer: 'stringify()',},{question: 'Which of the following keywords is used to define a variable in Javascript?',choices: ['var', 'let', 'var and let', 'None of the above'],type: 'MCQs',correctAnswer: 'var and let',},{question:'Which of the following methods can be used to display data in some form using Javascript?',choices: ['document.write()', 'console.log()', 'window.alert', 'All of the above'],type: 'MCQs',correctAnswer: 'All of the above',},{question: 'How can a datatype be declared to be a constant type?',choices: ['const', 'var', 'let', 'constant'],type: 'MCQs',correctAnswer: 'const',},],}
The above questions have been taken from interviewbit.
Quiz Question Structure Explanation
In my observation, all quiz APIs
have almost the same structure. question, choices, correct answers, and question type. These are the main keys.
This is the design we are going to implement, Codepen demo is also available at the end of the article.
Step-by-Step Quiz App Development
So, from here, we will see code blocks for each step. Let us get started.
Step 01: ReactJS Quiz Component
Quiz component will mainly have 3 elements for quiz UI
- Question
- Answers
- Next Button
Lets’s start writing quiz component in Reactjs
We have defined 3 states:
activeQuestions
, keep track of current questionselectedAnswer
, which answer user has selectedresult
, to calculate totalscores
,correctAnswers
,wrongAnswers
.
const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const [result, setResult] = useState({score: 0,correctAnswers: 0,wrongAnswers: 0,})return <h1>Quiz</h1>}export default Quiz
Step 02: Getting Questions From Question File
In this step, our goal is to display the question from the question file on the screen. Since the question file is an array
, we can use an index
to access particular array item (question). The code to get the first question is listed below.
Get first question
const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const { questions } = quizreturn (<div><h1>Quiz</h1><h2>{questions[activeQuestion].question}</p></div>)}export default Quiz
Info
Javascript array starts from index 0, so activeQuestion
initial state is 0.
Step 03: Fetching Answers
In our quiz question file, each question contains answers too, that is an array so we can render all the answers using the javascript map function
const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const { questions } = quiz// destructuringconst { question, choices } = questions[activeQuestion]return (<div><h1>Quiz</h1><h2>{question}</h2><ul>{choices.map((item) => (<li>{item}</li>))}</ul></div>)}
Step 04: Moving To The Next Question
On clicking the next button we will evaluate the following things:
- Adding one to
activeQuestion
(to move to the next question) - To check if the selected answer is right or wrong.
- Based on selected answer, we are updating the result screen, adding score to the result state, no. of right and wrong answers.
const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const onClickNext = () => {setActiveQuestion((prev) => prev + 1)}const { questions } = quizconst { question, choices } = questions[activeQuestion]return (<div><h1>Quiz</h1><h2>{question}</h2><ul>{choices.map((item) => (<li>{item}</li>))}</ul><button onClick={onClickNext}>Next</button></div>)}
We haven’t added any styling yet.
onClickNext
function we calculate the user score on basis of selected answer.
The use of the onClickNext
method is noticeable in the code. Depending on the selected answer determines the user's score. The score will increase by 5, and the number of right answers will increase by one if the chosen option is accurate. If not, it will leave the outcome unchanged and add one to the incorrect responses.
const onClickNext = () => {setActiveQuestion((prev) => prev + 1)setResult((prev) =>selectedAnswer? {...prev,score: prev.score + 5,correctAnswers: prev.correctAnswers + 1,}: { ...prev, wrongAnswers: prev.wrongAnswers + 1 })}
Logic For Selected Answer
As we have discussed above our Javascript questions file contains the answers too for each question.
When the user selects the answer, we will match if the selected answer matches the answer given in question file.
const onAnswerSelected = (answer) => {if (answer === correctAnswer) {setSelectedAnswer(true)console.log('right')} else {setSelectedAnswer(false)console.log('wrong')}}return (<div className="quiz-container"><h1>Quiz</h1><h2>{question}</h2><ul>{choices.map((answer) => (<li onClick={() => onAnswerSelected(answer)} key={answer}>{answer}</li>))}</ul><button onClick={onClickNext}>Next</button></div>)
This is what our UI looks like so far but some ingredients are still missing.
Step 05: Performance And UX Improvements
a. We want to highlight the selected answer by changing its UI.
b. In case of last question, we will show Finish as button text instead of Next.
c. Disable the next button if answer is not selected.
d. Adding 0 before question counter on top.
Let's implement them one by one.
a. Highlight the selected answer color
In the above result, you may have noticed we are not having any visuals for selected answer. Let's add this.
For this we have added another state selectedAnswerIndex
, so highlight only selected answer.
const [selectedAnswerIndex, setSelectedAnswerIndex] = useState(null)const onAnswerSelected = (answer, index) => {setSelectedAnswerIndex(index)if (answer === correctAnswer) {setSelectedAnswer(true)} else {setSelectedAnswer(false)}}
<ul>{choices.map((answer, index) => (<lionClick={() => onAnswerSelected(answer, index)}key={answer}className={selectedAnswerIndex === index ? 'selected-answer' : null}>{answer}</li>))}</ul>
CSS
.quiz-container ul .selected-answer {background: #ffd6ff;border: 1px solid #800080;}
b. Change button title if it is last question
In case of last question change button title to Finish from Next.
<button onClick={onClickNext}>{activeQuestion === questions.length - 1 ? 'Finish' : 'Next'}</button>
c. Disable Next button if answer is not selected
if selectedAnswerIndex
is null, means user haven’t clicked any answer, button will be disabled.
<button onClick={onClickNext} disabled={selectedAnswerIndex === null}>{activeQuestion === questions.length - 1 ? 'Finish' : 'Next'}</button>
d. Add leading zero
Add a 0 to the question number less than 10.
const addLeadingZero = (number) => (number > 9 ? number : `0${number}`)
Now UI and UX are so much improved.
Code Uptill now
import { useState } from 'react'import { quiz } from '../../data/questions'import './quiz.css'const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const [selectedAnswerIndex, setSelectedAnswerIndex] = useState(null)const [result, setResult] = useState({score: 0,correctAnswers: 0,wrongAnswers: 0,})const { questions } = quizconst { question, choices, correctAnswer } = questions[activeQuestion]const onClickNext = () => {// again reset the selectedAnwerIndex, so it won't effect next questionsetSelectedAnswerIndex(null)setActiveQuestion((prev) => prev + 1)setResult((prev) =>selectedAnswer? {...prev,score: prev.score + 5,correctAnswers: prev.correctAnswers + 1,}: { ...prev, wrongAnswers: prev.wrongAnswers + 1 })}const onAnswerSelected = (answer, index) => {setSelectedAnswerIndex(index)if (answer === correctAnswer) {setSelectedAnswer(true)} else {setSelectedAnswer(false)}}const addLeadingZero = (number) => (number > 9 ? number : `0${number}`)return (<div className="quiz-container"><div><span className="active-question-no">{addLeadingZero(activeQuestion + 1)}</span><span className="total-question">/{addLeadingZero(questions.length)}</span></div><h2>{question}</h2><ul>{choices.map((answer, index) => (<lionClick={() => onAnswerSelected(answer, index)}key={answer}className={selectedAnswerIndex === index ? 'selected-answer' : null}>{answer}</li>))}</ul><div className="flex-right"><button onClick={onClickNext} disabled={selectedAnswerIndex === null}>{activeQuestion === questions.length - 1 ? 'Finish' : 'Next'}</button></div></div>)}export default Quiz
Now we are all set to show the result component
Step 06: Result Screen
The result screen will actually a condition based, as soon all the questions finish we will show the result component.
In result screen, we want to show 4 things,
- No. of questions
- Total Score we get
- How many questions were correct
- How many questions were wrong
First of all, we will define a showResult
state, that will be false
initially.
const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const [showResult, setShowResult] = useState(false)const [selectedAnswerIndex, setSelectedAnswerIndex] = useState(null)..........
Become a better JavaScript developer by practicing only 10–20 minutes a week
Every Tuesday, you'll receive:
- A JavaScript problem-solving challenge
- Three conceptual JavaScript MCQs
Logic to show the result
When all the questions will be finished showResult
will become true.
Then we wrote a logic in jsx
if showResult
is true, show the result in quiz-container
otherwise quiz questions.
return (<div className="quiz-container">{!showResult ? (<div>.... {/* quiz question logic */}</div>) : (<div className="result"><h3>Result</h3><p>Total Question: <span>{questions.length}</span></p><p>Total Score:<span> {result.score}</span></p><p>Correct Answers:<span> {result.correctAnswers}</span></p><p>Wrong Answers:<span> {result.wrongAnswers}</span></p></div>)}</div>)}
We also added a condition on onClickNext
function.
const onClickNext = () => {setSelectedAnswerIndex(null)setResult((prev) =>selectedAnswer? {...prev,score: prev.score + 5,correctAnswers: prev.correctAnswers + 1,}: { ...prev, wrongAnswers: prev.wrongAnswers + 1 })if (activeQuestion !== questions.length - 1) {setActiveQuestion((prev) => prev + 1)} else {setActiveQuestion(0)setShowResult(true)}}
Here we are saying when activeQuestion !== questions.length - 1
means it is not the last question, add one to activeQuestion
if it is last question showResult
will be true
.
if you like you can move result jsx
to a separate component and pass data via props.
This is how our result component UI will look like.
Complete Component of Reactjs Quiz App
import { useState } from 'react'import { quiz } from '../../data/questions'import './quiz.css'const Quiz = () => {const [activeQuestion, setActiveQuestion] = useState(0)const [selectedAnswer, setSelectedAnswer] = useState('')const [showResult, setShowResult] = useState(false)const [selectedAnswerIndex, setSelectedAnswerIndex] = useState(null)const [result, setResult] = useState({score: 0,correctAnswers: 0,wrongAnswers: 0,})const { questions } = quizconst { question, choices, correctAnswer } = questions[activeQuestion]const onClickNext = () => {setSelectedAnswerIndex(null)setResult((prev) =>selectedAnswer? {...prev,score: prev.score + 5,correctAnswers: prev.correctAnswers + 1,}: { ...prev, wrongAnswers: prev.wrongAnswers + 1 })if (activeQuestion !== questions.length - 1) {setActiveQuestion((prev) => prev + 1)} else {setActiveQuestion(0)setShowResult(true)}}const onAnswerSelected = (answer, index) => {setSelectedAnswerIndex(index)if (answer === correctAnswer) {setSelectedAnswer(true)} else {setSelectedAnswer(false)}}const addLeadingZero = (number) => (number > 9 ? number : `0${number}`)return (<div className="quiz-container">{!showResult ? (<div><div><span className="active-question-no">{addLeadingZero(activeQuestion + 1)}</span><span className="total-question">/{addLeadingZero(questions.length)}</span></div><h2>{question}</h2><ul>{choices.map((answer, index) => (<lionClick={() => onAnswerSelected(answer, index)}key={answer}className={selectedAnswerIndex === index ? 'selected-answer' : null}>{answer}</li>))}</ul><div className="flex-right"><button onClick={onClickNext} disabled={selectedAnswerIndex === null}>{activeQuestion === questions.length - 1 ? 'Finish' : 'Next'}</button></div></div>) : (<div className="result"><h3>Result</h3><p>Total Question: <span>{questions.length}</span></p><p>Total Score:<span> {result.score}</span></p><p>Correct Answers:<span> {result.correctAnswers}</span></p><p>Wrong Answers:<span> {result.wrongAnswers}</span></p></div>)}</div>)}export default Quiz
CSS
@import url('https://fonts.googleapis.com/css2?family=Anek+Malayalam:wght@100;200;300;400;500;600;700;800&display=swap');body {font-family: 'Anek Malayalam', sans-serif;background: linear-gradient(90.04deg, #800080 0.03%, #ffc0cb 99.96%);color: #11052c;display: flex;justify-content: center;margin: 0;padding: 0 30px;}.quiz-container {max-width: 500px;min-width: 250px;background: #ffffff;border-radius: 4px;margin-top: 100px;padding: 30px 60px;}.quiz-container .active-question-no {font-size: 32px;font-weight: 500;color: #800080;}.quiz-container .total-question {font-size: 16px;font-weight: 500;color: #e0dee3;}.quiz-container h2 {font-size: 20px;font-weight: 500;margin: 0;}.quiz-container ul {margin-top: 20px;margin-left: -40px;}.quiz-container ul li {text-decoration: none;list-style: none;color: #2d264b;font-size: 16px;background: #ffffff;border: 1px solid #eaeaea;border-radius: 16px;padding: 11px;margin-top: 15px;cursor: pointer;}.quiz-container ul .selected-answer {background: #ffd6ff;border: 1px solid #800080;}.quiz-container button {background: linear-gradient(90.04deg, #800080 0.03%, #ffc0cb 99.96%);border-radius: 9px;font-size: 18px;color: #ffffff;padding: 10px 42px;outline: none;border: none;cursor: pointer;margin-top: 15px;}.quiz-container button:disabled {background: #e7e8e9;color: #9fa3a9;cursor: not-allowed;}.flex-right {display: flex;justify-content: flex-end;}.result h3 {font-size: 24px;letter-spacing: 1.4px;text-align: center;}.result p {font-size: 16px;font-weight: 500;}.result p span {color: #800080;font-size: 22px;}
Apply What You've Learned
This quiz app is missing a timer. You can improve this quiz app by adding a timer. However, I would suggest adding a timer with a twist.
For example, for each question the user will have 7 seconds to answer. If they fail to respond within 7 seconds, the app would automatically proceed to the next question.
Read more details of 7 Second Quiz Challenge to practice what you have learned.
React Quiz App Template
I've developed a Quiz App Template called "Xeven Quiz" to help you get started. This template is designed with ReactJS, Typescript and Styled Components that offers various question types, including Multiple Choice Questions (MCQs), Multiple Answer Questions (MAQs), and True/False questions.
With Xeven Quiz, users can easily take the quiz and see their results. The result screen shows how many questions the user attempted, how much they scored, how long it took, and whether they passed or failed.
In the result screen, users can see which questions they answered correctly and which ones were wrong. If the user provided a wrong answer, they can find the correct answer for that particular question.
To experience the Demo App, visit the link.
The React Quiz app template also has clear code documentation on GitHub. Feel free to check it out and use it as a starting point for your own quiz app project.