Download Laravel App
https://laravel.com/docs/12.x/installation
Connecting our Database
open .env file root directory.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel12DB
DB_USERNAME=root
DB_PASSWORD=root
Database Migration
php artisan migrate
myapp>php artisan migrate
Migration table created successfully.
check database table
php artisan make:controller HomeController change it with the following codes:
app\Http\Controllers\HomeController.php
//app\Http\Controllers\HomeController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Inertia\Inertia; use App\Models\Question; class HomeController extends Controller { public function get_home_data() { //$question = Question::latest()->get(); $question = Question::where('category', "Programming")->latest()->get(); return Inertia::render('quiz', [ 'questionsitems' => $question, ]); #return Inertia::render('quiz'); } }Create tables Question Model
php artisan make:model Question -m myapp>php artisan make:model Question -m
Open new Questions migrations yourproject/database/migrations laravelproject\database\migrations\_create_questions_table.php
//laravelproject\database\migrations\_create_questions_table.php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. */ public function up(): void { Schema::create('questions', function (Blueprint $table) { $table->id(); $table->string('question'); $table->json('options'); $table->string('answer'); $table->string('category'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('questions'); } };myapp>php artisan migrate
Migration table created successfully.
check database table
update Question Model
app/models/Question.php
namespace App\Models; use Illuminate\Database\Eloquent\Model; class Question extends Model { protected $fillable = [ 'question', 'options', 'answer', 'category', ]; protected $casts = [ 'options' => 'array', // Automatically casts to/from array/JSON ]; }Frontend with React and InertiaJS
File: pages\quiz.tsx
reactjs\resources\js\pages\quiz.tsx
//reactjs\resources\js\pages\quiz.tsx 'use client'; import { Head } from '@inertiajs/react'; import { useState } from 'react'; import QuestionCard from "@/components/QuestionCard" import Confetti from "react-confetti"; interface QuestionItem { id: number; question: string; options: string; answer: string; category: string; } interface Props { questionsitems: QuestionItem[]; } export default function quiz({ questionsitems }: Props) { //console.log(questionsitems) const [currentQuestion, setCurrentQuestion] = useState(0); const [selectedAnswer, setSelectedAnswer] = useState(null); const [score, setScore] = useState(0); const [isFinished, setIsFinished] = useState(false); const [showFeedback, setShowFeedback] = useState(false); const handleAnswer = (option) => { if (showFeedback) return; setSelectedAnswer(option); setShowFeedback(true); if (option === questionsitems[currentQuestion].answer) { setScore(score + 1); } }; const goToNext = () => { if (currentQuestion + 1 < questionsitems.length) { setCurrentQuestion(currentQuestion + 1); setSelectedAnswer(null); setShowFeedback(false); } else { setIsFinished(true); } }; const restartQuiz = () => { setCurrentQuestion(0); setScore(0); setSelectedAnswer(null); setShowFeedback(false); setIsFinished(false); }; const calculateProgress = () => { if (isFinished) return 100; const baseProgress = (currentQuestion / questionsitems.length) * 100; const questionProgress = selectedAnswer ? (1 / questionsitems.length) * 100 : 0; return baseProgress + questionProgress; }; const percentage = (score / questionsitems.length) * 100; const showConfetti = isFinished && percentage > 50; return ( <div className="min-h-screen"> <Head title="Quiz"></Head> <div className="min-h-screen bg-gray-900 text-white flex flex-col items-center justify-center p-4 "> {showConfetti && <Confetti />} <div className="text-center mb-8"> <h1 className="text-4xl font-bold text-purple-600 mb-2">Laravel 12 Quiz React | React Starter Kit</h1> <p className="text-gray-400">Test your knowledge</p> </div> <div className="w-full max-w-xl mb-6"> <div className="bg-gray-700 h-3 rounded-full overflow-hidden"> <div className="h-full bg-gradient-to-r from-indigo-500 to-purple-600 duration-500 ease-out transition-all" style={{ width: `${calculateProgress()}%` }} ></div> </div> </div> {!isFinished ? ( <> <QuestionCard showFeedback={showFeedback} onAnswer={handleAnswer} data={questionsitems[currentQuestion]} current={currentQuestion} total={questionsitems.length} selected={selectedAnswer} /> <div className="mt-6 min-h-[60px]"> {showFeedback && ( <button className="bg-gradient-to-r from-indigo-600 to-purple-600 py-3 px-6 rounded-lg font-medium shadow-lg cursor-pointer" onClick={goToNext} > {currentQuestion + 1 < questionsitems.length ? "Continue" : "See Results"} </button> )} </div> </> ) : ( <div className="text-center"> <h2 className="text-3xl font-bold mb-4">Quiz Completed!</h2> <p className="text-xl mb-6"> You scored <span>{score}</span> out of{" "} <span className="font-bold">{questionsitems.length}</span> and it is{" "} {Math.round((score / questionsitems.length) * 100)}% </p> <button className="bg-gradient-to-r from-indigo-600 to-purple-600 py-3 px-6 rounded-lg font-medium shadow-lg cursor-pointer" onClick={restartQuiz} > Restart Quiz </button> </div> )} </div> </div> ); }File: js\components\QuestionCard.tsx
reactjs\resources\js\components\QuestionCard.tsx
//reactjs\resources\js\components\QuestionCard.tsx const QuestionCard = ({ data, onAnswer, showFeedback, selected, current, total, }) => { console.log(data) const { question, options, answer } = data; const getButtonStyle = (option) => { if (!showFeedback) { return "bg-indigo-700 hover:bg-indigo-600 hover:scale-[1.01]"; } if (option === answer) return "bg-emerald-600"; if (option === selected) return "bg-rose-600"; return "bg-gray-600"; }; return ( <div className="bg-gray-800 p-6 rounded-2xl shadow-lg w-full max-w-xl border border-gray-700"> <div className="flex justify-between items-center mb-4"> <h2 className="text-lg font-medium text-gray-300"> Question {current + 1} of {total} </h2> <span className="text-sm bg-gray-700 px-3 py-1 rounded-full"> {selected ? Math.round(((current + 1) / total) * 100) + "% complete" : Math.round((current / total) * 100) + "% complete"} </span> </div> <p className="text-xl font-medium mb-6">{question}</p> <div className="grid gap-3"> {options.map((option, index) => ( <button className={`${getButtonStyle( option )} text-left px-4 py-3 cursor-pointer rounded-lg text-white `} key={index} onClick={() => onAnswer(option)} disabled={showFeedback} > {option} </button> ))} </div> </div> ); }; export default QuestionCard;
//routes/web.php use Illuminate\Support\Facades\Route; use Inertia\Inertia; use App\Http\Controllers\HomeController; Route::get('/', function () { return Inertia::render('welcome'); })->name('home'); Route::get('/quiz', [HomeController::class, 'get_home_data'])->name('quiz'); Route::middleware(['auth', 'verified'])->group(function () { Route::get('dashboard', function () { return Inertia::render('dashboard'); })->name('dashboard'); });Run php artisan serve and npm run dev myapp>composer run dev
Starting Laravel development server: http://127.0.0.1:8000