Tech With Galvan
Back to Articles
Development11/1/2024 • 3 min read

Interactive Quiz App using Python and Streamlit

Build an interactive quiz app with Python and Streamlit — session-state scoring, instant feedback, and results charts in under 100 lines.

Galvan
Galvan

Founder & Creator

Introduction

This project guides you through building an Interactive Quiz App using Python and Streamlit. The app presents multiple-choice questions one at a time, tracks the user’s score with st.session_state, and gives instant feedback after every answer. If you like data-driven apps, this quiz pairs naturally with the Pandas dashboard tutorial.

Prerequisites

pip install streamlit

Step 1: Create the Script

Create quiz_app.py and paste the following:

import streamlit as st

st.set_page_config(page_title="Quiz App", page_icon="🧠")
st.title("🧠 Interactive Quiz App")

QUESTIONS = [
    {
        "question": "What is the capital of France?",
        "options": ["Berlin", "Madrid", "Paris", "Rome"],
        "answer": "Paris",
    },
    {
        "question": "Which planet is known as the Red Planet?",
        "options": ["Earth", "Mars", "Jupiter", "Venus"],
        "answer": "Mars",
    },
    {
        "question": "What is the highest mountain in the world?",
        "options": ["K2", "Kangchenjunga", "Mount Everest", "Lhotse"],
        "answer": "Mount Everest",
    },
    {
        "question": "How many sides does a hexagon have?",
        "options": ["5", "6", "7", "8"],
        "answer": "6",
    },
    {
        "question": "Which language is primarily used for web styling?",
        "options": ["Python", "JavaScript", "CSS", "SQL"],
        "answer": "CSS",
    },
]

for key, default in [("score", 0), ("current", 0), ("finished", False)]:
    if key not in st.session_state:
        st.session_state[key] = default

if st.session_state.finished:
    total = len(QUESTIONS)
    score = st.session_state.score
    st.balloons()
    st.subheader("🎉 Quiz Complete!")
    st.metric("Your Score", f"{score} / {total}")
    pct = score / total * 100
    if pct >= 80:
        st.success("Excellent work! 🌟")
    elif pct >= 60:
        st.info("Good job! Keep practising. 📚")
    else:
        st.warning("Keep trying — you will get there! 💪")
    if st.button("🔄 Restart Quiz"):
        st.session_state.update({"score": 0, "current": 0, "finished": False})
        st.rerun()
else:
    idx = st.session_state.current
    q = QUESTIONS[idx]
    st.progress(idx / len(QUESTIONS), text=f"Question {idx + 1} of {len(QUESTIONS)}")
    st.subheader(q["question"])
    selected = st.radio("Choose your answer:", q["options"], key=f"q_{idx}")
    if st.button("Submit Answer"):
        if selected == q["answer"]:
            st.success("✅ Correct!")
            st.session_state.score += 1
        else:
            st.error(f"❌ Wrong! The correct answer was **{q['answer']}**.")
        st.session_state.current += 1
        if st.session_state.current >= len(QUESTIONS):
            st.session_state.finished = True
        st.rerun()

Step 2: Run the App

streamlit run quiz_app.py

Step 3: Use the App

The quiz shows one question at a time with a progress bar at the top. Select an answer and click Submit Answer to get instant feedback. After the final question the score screen appears with a performance message.

How It Works

The quiz is a state machine: questions live in a Python list of dicts, and st.session_state["q_num"] tracks where the user is. Each rerun renders the current question with st.radio, and the Submit button checks the answer, stores the result, and increments the counter — triggering a rerun that shows the next question.

Because Streamlit re-executes the whole script per interaction, all progress must live in st.session_state; anything stored in a plain variable evaporates on rerun. That single concept — session state as the app’s memory — is the core lesson of this project.

At the end, results render as a score plus st.balloons(). With a few more lines the answers list becomes a DataFrame and you get per-question analytics with st.bar_chart, exactly the technique from the data dashboard.

Key Concepts

  • st.session_state — Persists the score and current question index across app reruns.
  • st.radio() — Renders the multiple-choice options as radio buttons.
  • st.progress() — Displays a progress bar showing how far through the quiz the user is.
  • st.rerun() — Re-runs the script to advance to the next question instantly.
  • st.balloons() — Plays a fun balloon animation on the results screen.

What to Try Next

  • Load questions from a JSON file so you can add new ones without editing code.
  • Add a countdown timer for each question using time.time().
  • Show a brief explanation for each answer after the user submits.
  • Save high scores to a CSV file and display a leaderboard with pandas.

Common Errors & Fixes

  • Quiz restarts on every answer — the question index is a local variable instead of st.session_state.

  • Double-submit counts twice — the button check runs before the state increments; use the on_click callback form or guard with if st.button(...) and not answered.

  • KeyError: 'q_num' — session keys must be initialized before first read: st.session_state.setdefault("q_num", 0).

  • Radio shows previous selection — give the radio a key and reset it when advancing questions.

FAQ

How do I load questions from a file?

Read a JSON/CSV into the questions list at startup — the structure matches the blog content files pattern: one object per question.

Can I add a timer per question?

Yes — record time.time() when the question renders and compare on submit; Streamlit’s st_autorefresh component can force the check.

How do I show a results breakdown?

Convert the results list to a DataFrame and use st.bar_chart(df) — five lines, covered in the dashboard tutorial.