Skip to main content
RapidDev - Software Development Agency
App Featuresforms-data19 min read

How to Add a Knowledge Quiz to Your App (Copy-Paste Prompts Included)

A knowledge quiz needs a React state machine managing question flow and scoring, server-side answer validation so correct answers are never exposed to the client, and a Supabase table for results and leaderboard. With V0 or Lovable you can ship a working quiz with a timer, score reveal animation, and leaderboard in 4–8 hours for $0–$25/month. All rendering libraries (Recharts, Framer Motion, canvas-confetti) are free.

4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members

Feature spec

Beginner

Category

forms-data

Build with AI

4–8 hours with Lovable or V0

Custom build

3–5 days custom dev

Running cost

$0/mo at launch · $0–$25/mo at scale

Works on

WebMobile

Everything it takes to ship an Interactive Knowledge Quiz — parts, prompts, and real costs.

TL;DR

A knowledge quiz needs a React state machine managing question flow and scoring, server-side answer validation so correct answers are never exposed to the client, and a Supabase table for results and leaderboard. With V0 or Lovable you can ship a working quiz with a timer, score reveal animation, and leaderboard in 4–8 hours for $0–$25/month. All rendering libraries (Recharts, Framer Motion, canvas-confetti) are free.

What an Interactive Knowledge Quiz Actually Is

An interactive knowledge quiz presents a sequence of questions, collects answers one at a time, provides immediate feedback on each selection, and delivers a scored result at the end. A SaaS onboarding quiz tests whether users understand a feature before unlocking it. A learning platform assesses topic mastery and suggests resources for missed questions. A community app runs weekly trivia with a live leaderboard. The architecture is the same across all three: a client-side state machine that drives the question sequence, server-side answer validation to prevent cheating (correct answers must never travel to the browser), and a Supabase results table that powers both the user's history and the public leaderboard.

What users consider table stakes in 2026

  • Immediate visual feedback on answer selection — the selected option highlights correct in green or wrong in red before the user moves to the next question, without a full-page reload
  • A progress bar or 'Question 3 of 10' counter visible at all times so users know how much is left
  • A timer per quiz (total countdown) or per question (auto-advance on timeout) with a visible countdown and color change in the final 10 seconds
  • An animated score reveal at the end: percentage, pass/fail label, and a Recharts breakdown by topic or category so users see exactly where they lost points
  • A wrong-answer review mode where users can revisit every question and see their answer, the correct answer, and the explanation text
  • A share button that lets users post their score to social media or copy a shareable link with the score embedded in the URL

Anatomy of the Feature

Six components — the state machine and server-side validation are where first builds most commonly break. The score reveal and share widget add polish that dramatically increases completion and re-play rates.

Layers:UIDataBackend

Question renderer

UI

A React component rendering question text and answer options as clickable cards or a RadioGroup. Framer Motion AnimatePresence handles slide-in transitions between questions. shadcn/ui Card and RadioGroup provide the base styling. After an answer is selected, the correct option highlights green and the wrong selected option highlights red before advancing.

Note: Lock the answer selection after one click — add a boolean `isAnswered` state that disables all option clicks after the first selection. Without this, users can change their answer during the feedback display.

Quiz state machine

UI

A React useReducer managing the full quiz lifecycle: { currentIndex, answers, score, timeRemaining, status: 'idle | in-progress | reviewing | complete' }. Transitions: START_QUIZ, ANSWER_QUESTION, NEXT_QUESTION, COMPLETE_QUIZ, START_REVIEW. The timer runs in a useEffect + setInterval and dispatches a TIMEOUT action when timeRemaining reaches zero.

Note: Clear the setInterval in the useEffect cleanup function to prevent 'Cannot update state on unmounted component' errors. Check an isActive ref before calling setState in the interval callback.

Score calculator

Data

For simple quizzes: client-side scoring compares submitted answers against correct answers returned after quiz completion. For cheating-resistant scoring: a Supabase Edge Function receives the user's answers and validates them against server-stored correct_answer values — the correct answers are never sent to the client in the questions query.

Note: The column-level RLS policy on the questions table is the key mechanism: the public SELECT policy explicitly excludes the correct_answer column. Only the Edge Function (using the service role key) can read correct answers.

Results + breakdown page

UI

Score reveal shows the percentage, a pass/fail label against the quiz's pass_score_pct, and a Recharts BarChart or RadarChart breaking down performance by topic/category. canvas-confetti triggers a 3-second animation on passing scores. The wrong-answer review list renders each question, the user's selected answer, the correct answer, and the explanation text.

Note: canvas-confetti is a vanilla JS library — call it via a useEffect on mount of the results page when the score exceeds the pass threshold. No React wrapper is needed.

Leaderboard

Backend

A Supabase query on the quiz_results table ordering by score DESC and limiting to 10 rows. An optional Supabase Realtime subscription updates the leaderboard live as new results are inserted. Each row shows rank, username, score, and completion time.

Note: If the leaderboard is public, create a Supabase Database Function (RPC) that returns the top 10 with user display names joined from the profiles table — avoid exposing raw user IDs in the public query.

Share widget

UI

The Web Share API (navigator.share) on mobile browsers opens the native share sheet. On desktop, a clipboard copy button saves a URL like /quiz/results?score=85&quiz=trivia-week-12. In Next.js, Vercel OG generates a score card image for social sharing using the quiz name and score as URL parameters.

Note: Check navigator.share !== undefined before calling it — the Web Share API is not available in all desktop browsers. Fall back to clipboard copy silently.

The data model

Three tables: quizzes stores quiz metadata, questions stores questions with correct answers (column excluded from public reads by RLS), and quiz_results stores all submitted scores. Run this in the Supabase SQL editor:

schema.sql
1create table public.quizzes (
2 id uuid primary key default gen_random_uuid(),
3 title text not null,
4 description text,
5 time_limit_seconds int,
6 pass_score_pct int not null default 70,
7 is_public boolean not null default true,
8 created_at timestamptz not null default now()
9);
10
11create table public.questions (
12 id uuid primary key default gen_random_uuid(),
13 quiz_id uuid references public.quizzes(id) on delete cascade not null,
14 text text not null,
15 type text not null default 'multiple_choice' check (type in ('multiple_choice', 'true_false', 'short_answer')),
16 options jsonb not null default '[]',
17 correct_answer text not null,
18 explanation text,
19 category text,
20 sort_order int not null default 0
21);
22
23create table public.quiz_results (
24 id uuid primary key default gen_random_uuid(),
25 quiz_id uuid references public.quizzes(id) on delete cascade not null,
26 user_id uuid references auth.users(id) on delete set null,
27 score int not null,
28 total_questions int not null,
29 answers_taken jsonb not null default '{}',
30 passed boolean not null default false,
31 completed_at timestamptz not null default now()
32);
33
34create index quiz_results_leaderboard_idx
35 on public.quiz_results (quiz_id, score desc, completed_at asc);
36
37create index quiz_results_user_idx
38 on public.quiz_results (user_id, completed_at desc);
39
40alter table public.quizzes enable row level security;
41alter table public.questions enable row level security;
42alter table public.quiz_results enable row level security;
43
44create policy "Public can read public quizzes"
45 on public.quizzes for select
46 using (is_public = true);
47
48create policy "Public can read questions without correct answers"
49 on public.questions for select
50 using (true);
51
52create policy "Users can insert own results"
53 on public.quiz_results for insert
54 with check (auth.uid() = user_id);
55
56create policy "Users can read own results"
57 on public.quiz_results for select
58 using (auth.uid() = user_id);
59
60create policy "Public can read quiz results for leaderboard"
61 on public.quiz_results for select
62 using (true);

Heads up: IMPORTANT: The questions SELECT policy allows public access to the questions table, but you must exclude the correct_answer column in your client query — use `select('id, text, type, options, explanation, category, sort_order')` and never include correct_answer. Answer validation happens exclusively in a Supabase Edge Function that uses the service role key to read correct_answer values. This is the only architecture that prevents browser DevTools from revealing answers.

Build it — pick your path

Each tool fits this feature differently. Switch tabs to compare fit, time, the exact steps, and a copy-paste prompt.

Best UI, Next.js ecosystemFit for this feature:

V0 produces the highest UI quality for web quizzes — polished card layouts, Recharts score breakdowns, shadcn/ui RadioGroups. Next.js Server Actions validate answers securely server-side without a separate Edge Function. OG image generation for score sharing is native to Next.js.

Step by step

  1. 1Prompt V0 with the spec below to generate the quiz page, results page, and leaderboard component
  2. 2Add Supabase environment variables in the V0 Vars panel: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, and SUPABASE_SERVICE_ROLE_KEY (for the Server Action answer validation)
  3. 3Run the SQL schema from this page in the Supabase SQL editor
  4. 4Verify the Server Action that validates answers uses the service role Supabase client, not the anon client, to read correct_answer values
  5. 5Add an /api/og route for the score card OG image using Vercel's @vercel/og package with the quiz name and score as parameters
  6. 6Connect to GitHub via the V0 Git panel and deploy to Vercel to test OG image generation and the Web Share API on a real device
Paste into v0
Build a Next.js interactive knowledge quiz at /quiz/[quizId]. Quiz page (client component): fetch quiz and questions from Supabase using the anon client — select only id, text, type, options, explanation, category, sort_order from questions (never correct_answer). Use useReducer for state: { currentIndex, answers: Record<string, string>, score: number, timeRemaining: number, status: 'idle' | 'in-progress' | 'reviewing' | 'complete' }. Timer: useEffect + setInterval decrementing timeRemaining each second; clear on unmount; auto-call submitQuiz action when it hits 0. One question at a time with AnimatePresence from framer-motion (mode='wait', key={currentIndex}). Answer options as shadcn/ui Button cards in a 2-column grid. On answer click: call a Next.js Server Action validateAnswer(questionId, selectedAnswer) that uses the Supabase service role client to read the correct_answer and return { correct: boolean, correctAnswer: string, explanation: string }. Highlight correct green, wrong red; show explanation for 2 seconds; then dispatch NEXT_QUESTION. Progress bar: shadcn/ui Progress component showing currentIndex/total. Results page /quiz/[quizId]/results: score %, pass/fail badge, Recharts BarChart of correct vs incorrect count by question.category, canvas-confetti effect on pass, wrong-answer review accordion. Leaderboard: top 10 quiz_results from Supabase ordered by score DESC then completed_at ASC. Share: navigator.share with title+score URL; fallback to clipboard. OG image: /api/og?quiz=[title]&score=[pct] using @vercel/og. Insert quiz_results row via Server Action on completion. Add beforeunload listener warning 'Your progress will be lost'.

Where this path bites

  • V0 will not auto-provision the Supabase tsvector or Realtime subscription — run the schema and configure Realtime manually
  • Leaderboard Realtime updates need explicit prompt instruction; V0 defaults to a static fetch without a subscription
  • OG image route requires a separate Vercel deployment to work — it cannot be tested in V0's sandbox preview

Third-party services you'll need

All quiz rendering and animation libraries are free. Supabase handles results storage and the optional leaderboard at no cost at launch. Vercel OG is included with Vercel deployments:

ServiceWhat it doesFree tierPaid from
SupabaseQuiz and question storage, quiz results for leaderboard and history, optional Realtime subscription for live leaderboard updatesFree (500MB, 2 projects)Pro $25/mo (8GB DB)
Vercel OGScore card image generation for social sharing — produces a pre-rendered PNG of the quiz name and user score when the share URL is pasted into Twitter, Slack, or iMessageFree (included with Vercel deployment)Included in all Vercel plans
canvas-confettiClient-side confetti animation on passing score — zero-dependency, 7KB, runs entirely in the browser canvasFree (MIT license)Free

Swipe the table sideways to see pricing.

What it costs to run

Drag through the tiers to see how your monthly bill scales with users — no surprises later.

Estimated monthly running cost

$0/mo

Supabase free tier easily handles 100 users' quiz results and question data. All animation and chart libraries are free. Vercel OG is included in the free Vercel plan.

Estimates use verified 2026 pricing and assume typical usage per user. Your real bill depends on activity, storage, and third-party plans.

What breaks when AI tools build this

The failures people actually hit on their first build — the symptom, why it happens, and the exact fix.

Users can open browser DevTools and see correct answers in the network response

Symptom: AI tools default to fetching the full questions row — including the correct_answer column — in the client query. This means any user who opens the Network tab in DevTools can read all correct answers before the quiz starts. The quiz integrity is completely compromised.

Fix: In the Supabase client query, explicitly select only safe columns: `select('id, text, type, options, explanation, category, sort_order')`. Move answer validation to a Supabase Edge Function or Next.js Server Action that reads correct_answer using the service role key — the service role key is never exposed to the browser. After generation, verify the client-side network calls do not include correct_answer in any response.

Timer keeps running after quiz submission, causing a React state update on unmounted component error

Symptom: The setInterval timer is started in a useEffect but the cleanup function is missing or incomplete. When the quiz completes and the component unmounts (or navigates to the results page), the interval keeps firing and tries to update state on the now-unmounted component. React throws a warning and in some cases the results page receives incorrect time data.

Fix: Return a cleanup function from the timer useEffect: `return () => clearInterval(timerId)`. Additionally, create a local `isActive` ref (useRef(true)) and check `if (isActive.current)` before calling dispatch in the interval callback. Set `isActive.current = false` in the cleanup. Both guards are needed.

Score sharing link shows 'undefined' in the OG image

Symptom: V0 or Lovable generates a share URL that reads the score from a URL search parameter, but the parameter is not URL-encoded before being appended to the URL. When the score contains special characters (or when the OG route receives an empty param), it renders 'undefined' in the social share card image.

Fix: Encode the score and quiz name when building the share URL: `/api/og?quiz=${encodeURIComponent(quizTitle)}&score=${score}`. In the OG route handler, decode with `decodeURIComponent(searchParams.get('quiz') ?? '')` and add a fallback for null values before rendering.

Framer Motion question transitions cause layout shift — the next question jumps instead of sliding

Symptom: AnimatePresence requires two specific configurations to produce a smooth slide-in transition: a stable key prop on the animated element and mode='wait' on the AnimatePresence wrapper. Without mode='wait', the exiting question and entering question render simultaneously, causing a content overlap. Without a changing key, AnimatePresence doesn't detect that the content changed and skips the animation entirely.

Fix: Add key={currentQuestionIndex} to the question wrapper div inside AnimatePresence, and mode='wait' to the AnimatePresence component itself. The complete structure: `<AnimatePresence mode='wait'><motion.div key={currentQuestionIndex} initial={{ x: 100, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -100, opacity: 0 }}>`.

Best practices

1

Never send the correct_answer column to the client — validate all answers in a Server Action or Edge Function using the service role key, even for quizzes where cheating seems unlikely

2

Lock answer options after the first click by setting an isAnswered boolean in the question component state — allow one selection per question, no going back

3

Show the explanation text for every answer (correct and incorrect) before advancing — explanations are what makes a quiz feel educational rather than just a scoring exercise

4

Add a beforeunload event listener during the quiz to warn users their progress will be lost if they navigate away — especially important for timed quizzes

5

Cache the questions array for a quiz session after the initial fetch — re-fetching on each question navigation adds unnecessary latency and Supabase read counts

6

Build the wrong-answer review mode before launch — it is the most-requested feature after users complete a quiz and the primary reason they share results with others

7

For public quizzes, add a quiz result INSERT trigger that updates a cached top-10 leaderboard table rather than computing ORDER BY score DESC on every leaderboard view

When You Need Custom Development

V0 and Lovable handle standard scored quizzes well. These requirements push into territory where custom development adds real value:

  • Adaptive difficulty that adjusts which question is shown next based on the user's accuracy rate — requires an item bank with pre-calibrated difficulty parameters and IRT scoring logic
  • Anti-cheat proctoring: tab-switch detection, copy-paste blocking, or webcam monitoring for exam-integrity scenarios like certification tests or academic assessments
  • LMS integration: reporting scores as SCORM 1.2 or xAPI statements to Moodle, Canvas, Blackboard, or a corporate LRS requires a separate compliance layer
  • Certification issuance: generating a signed PDF certificate with the user's name, score, and date on pass requires server-side PDF generation and a tamper-evident signing mechanism

RapidDev builds these features for production

Real auth, database, and payments — fixed price, delivered in weeks. You own 100% of the code.

Get a fixed-price quote

Frequently asked questions

How do I stop users from cheating by inspecting the quiz answers in the browser?

Never include the correct_answer column in your client-side Supabase query. When the user submits an answer, call a Next.js Server Action or Supabase Edge Function that reads the correct answer using the service role key (which never leaves the server) and returns only a boolean correct/incorrect plus the explanation. The correct answer text is only revealed after it's too late to use it.

Can I add a timer to each quiz question in Lovable or V0?

Yes. Both tools can generate a useEffect timer that decrements a timeRemaining value in the quiz state machine. For per-question timers, reset timeRemaining to the question's allotted seconds every time currentIndex changes. For a total quiz timer, start it once at quiz load and auto-submit when it hits zero. Explicitly describe this behavior in your prompt — AI tools default to no timer.

How do I show correct answers after the quiz is submitted?

Build a review mode state ('reviewing') in your quiz state machine. When the quiz completes, the Edge Function or Server Action returns the correct answers for all questions as part of the final result payload. In review mode, render each question with the user's answer highlighted and the correct answer shown — safe to display client-side after submission since the quiz is over.

Can I add a leaderboard to a knowledge quiz?

Yes. Insert a row into quiz_results on completion with the user's score. Query top 10 rows ordered by score DESC for the leaderboard display. For a live leaderboard that updates as new scores come in, add a Supabase Realtime subscription on the quiz_results table filtered by quiz_id.

How do I let users share their quiz score on social media?

Two approaches: the Web Share API (navigator.share) opens the native share sheet on mobile with a title, description, and URL — check navigator.share !== undefined before calling it and fall back to clipboard copy on desktop. For rich social preview cards, add a Next.js /api/og route using @vercel/og that renders a score card image when the share URL is pasted into Twitter, Slack, or iMessage.

What's the best way to store quiz results in Supabase?

A quiz_results table with quiz_id, user_id, score, total_questions, passed (boolean), answers_taken (jsonb storing each question_id and the user's selected answer), and completed_at. Add a composite index on (quiz_id, score DESC) for fast leaderboard queries. Insert via a Server Action or Edge Function that also validates the final answers — never via a direct client insert, which would allow score manipulation.

Can I build a quiz with different question types — multiple choice and true/false?

Yes. Store the question type as a 'type' column ('multiple_choice', 'true_false', 'short_answer') and the answer options as a jsonb array. In the question renderer, use a conditional switch on type to render the appropriate input: RadioGroup cards for multiple choice, a two-option True/False toggle for boolean, or a text input for short answer. Server-side answer comparison handles each type's matching logic.

RapidDev

Need this feature production-ready?

RapidDev builds an interactive knowledge quiz into real apps — auth, database, payments — at $13K–$25K.

Book a free consultation

30-min call. No commitment.

Want this built for you?

We ship production apps at a fixed price — $13K–$25K, 6–10 weeks, source code yours. You've seen what it takes; we do it every week.

Get a fixed-price quote

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.