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
