Why Use the Livewire Starter Kit in Laravel 12?
Laravel 12 introduced a major shift in how starter kits work. Instead of bundling scaffolding inside the framework, Laravel now provides standalone starter kit repositories that you clone and customize. The Livewire starter kit is the recommended choice for developers who want to stay in the PHP ecosystem while building highly interactive UIs. Here is why it stands out:
- Official first-party kit maintained by the Laravel team
- Livewire 3 for reactive components without writing JavaScript
- Volt single-file components for cleaner, co-located PHP logic and Blade templates
- Tailwind CSS 4 with automatic content detection (no config needed)
- Flux UI — a beautiful, accessible component library built for Livewire
- Pre-built authentication pages: login, register, password reset, email verification, and profile settings
- Dark mode support out of the box

Table Of Content
1 Prerequisites
- PHP 8.2+
- Composer
- Node.js 20+ & NPM
- MySQL / MariaDB / SQLite
- Basic understanding of Laravel MVC pattern
- A terminal (VS Code terminal, iTerm, Windows Terminal, etc.)
2 Introduction
3 Install Laravel 12 Livewire Starter Kit
3.1 Create a New Project from the Starter Kit
laravel new task-manager --using=livewire
The installer will automatically clone the Livewire starter kit repository, install Composer dependencies, set up your .env file, and prompt you for database configuration.Alternatively, if you do not have the Laravel installer, you can clone the starter kit manually:
git clone https://github.com/laravel/livewire-starter-kit.git task-manager
cd task-manager
composer install
cp .env.example .env
php artisan key:generate
Then navigate to your project directory:
cd task-manager
3.2 Configure Database (.env)
Option A: SQLite (default — zero configuration)
DB_CONNECTION=sqlite
# DB_DATABASE is auto-set to database/database.sqlite
Option B: MySQL
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task_manager
DB_USERNAME=root
DB_PASSWORD=
If using MySQL, create the database task_manager in your MySQL server before proceeding.
3.3 Run Migrations & Start Dev Server
php artisan migrate
Install frontend dependencies and start the Vite dev server:
npm install
npm run dev
In a separate terminal, start the Laravel dev server:
php artisan serve
Open http://localhost:8000 in your browser. You should see the Laravel 12 welcome page with links to Register and Login. Try registering a new account — the entire authentication system is already working! 4 Understand the Starter Kit Structure
Authentication (pre-built and ready to use):
- resources/views/livewire/auth/login.blade.php — Login page (Volt component)
- resources/views/livewire/auth/register.blade.php — Registration page
- resources/views/livewire/auth/forgot-password.blade.php — Password reset request
- resources/views/livewire/auth/reset-password.blade.php — Password reset form
- resources/views/livewire/auth/verify-email.blade.php — Email verification
Settings Pages:
- resources/views/livewire/settings/profile.blade.php — Update name & email
- resources/views/livewire/settings/password.blade.php — Change password
- resources/views/livewire/settings/appearance.blade.php — Dark/light mode toggle
Layout & Core Files:
- resources/views/components/layouts/app.blade.php — Main authenticated layout
- resources/views/components/layouts/auth/ — Guest layout for login/register
- routes/web.php — All web routes
- routes/auth.php — Authentication routes (auto-included)
All authentication views are Volt single-file components — meaning the PHP logic and Blade template live in the same .blade.php file. This is one of the key architectural decisions in the Laravel 12 starter kit.
5 Livewire 3 & Volt Basics
Traditional Livewire 3 Components (Class + Blade):
// app/Livewire/Counter.php
<?php
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}
?>
<!-- resources/views/livewire/counter.blade.php -->
<div>
<h2>Count: {{ $count }}</h2>
<button wire:click="increment">+ Increment</button>
</div>
Volt Single-File Components (recommended by the starter kit):
Volt lets you put the PHP logic and Blade template in a single file using the @php ... @endphp block with the new class syntax:
<!-- resources/views/livewire/counter.blade.php -->
<?php
use Livewire\Volt\Component;
new class extends Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
}
};
?>
<div>
<h2>Count: {{ $count }}</h2>
<button wire:click="increment" class="px-4 py-2 bg-blue-500 text-white rounded">
+ Increment
</button>
</div>
Both approaches work equally well. The starter kit uses Volt for auth pages, and we will use the traditional class-based approach for our task manager since it is easier to understand for beginners and keeps larger components well-organized.
6 Create Task Migration & Model
php artisan make:model Task -m
Update the Migration FileOpen the generated migration in database/migrations/:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->text('description')->nullable();
$table->enum('priority', ['low', 'medium', 'high'])->default('medium');
$table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending');
$table->date('due_date')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('tasks');
}
};
?>
Run the migration:
php artisan migrate
Update the Task Model
Open app/Models/Task.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Task extends Model
{
protected $fillable = [
'user_id',
'title',
'description',
'priority',
'status',
'due_date',
];
protected function casts(): array
{
return [
'due_date' => 'date',
];
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function isOverdue(): bool
{
return $this->due_date && $this->due_date->isPast() && $this->status !== 'completed';
}
}
?>
Add the relationship to app/Models/User.php (add this method inside the User class):
use Illuminate\Database\Eloquent\Relations\HasMany;
public function tasks(): HasMany
{
return $this->hasMany(Task::class);
}
7 Build the Task Manager Livewire Component
php artisan make:livewire TaskManager
This creates two files: app/Livewire/TaskManager.php (class) and resources/views/livewire/task-manager.blade.php (view).Open app/Livewire/TaskManager.php and replace with the full component:
<?php
namespace App\Livewire;
use App\Models\Task;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use Livewire\WithPagination;
class TaskManager extends Component
{
use WithPagination;
// Form fields
public string $title = '';
public string $description = '';
public string $priority = 'medium';
public string $status = 'pending';
public string $due_date = '';
// Editing state
public ?int $editingTaskId = null;
// Search & filter
public string $search = '';
public string $filterStatus = '';
public string $filterPriority = '';
// Modal toggle
public bool $showModal = false;
protected function rules(): array
{
return [
'title' => 'required|string|min:3|max:255',
'description' => 'nullable|string|max:1000',
'priority' => 'required|in:low,medium,high',
'status' => 'required|in:pending,in_progress,completed',
'due_date' => 'nullable|date|after_or_equal:today',
];
}
protected function messages(): array
{
return [
'title.required' => 'Please enter a task title.',
'title.min' => 'Title must be at least 3 characters.',
'due_date.after_or_equal' => 'Due date cannot be in the past.',
];
}
public function openCreateModal(): void
{
$this->resetForm();
$this->showModal = true;
}
public function openEditModal(int $taskId): void
{
$task = Auth::user()->tasks()->findOrFail($taskId);
$this->editingTaskId = $task->id;
$this->title = $task->title;
$this->description = $task->description ?? '';
$this->priority = $task->priority;
$this->status = $task->status;
$this->due_date = $task->due_date ? $task->due_date->format('Y-m-d') : '';
$this->showModal = true;
}
public function save(): void
{
$this->validate();
$data = [
'title' => $this->title,
'description' => $this->description ?: null,
'priority' => $this->priority,
'status' => $this->status,
'due_date' => $this->due_date ?: null,
];
if ($this->editingTaskId) {
$task = Auth::user()->tasks()->findOrFail($this->editingTaskId);
$task->update($data);
session()->flash('success', 'Task updated successfully!');
} else {
Auth::user()->tasks()->create($data);
session()->flash('success', 'Task created successfully!');
}
$this->showModal = false;
$this->resetForm();
}
public function toggleStatus(int $taskId): void
{
$task = Auth::user()->tasks()->findOrFail($taskId);
$task->update([
'status' => $task->status === 'completed' ? 'pending' : 'completed',
]);
}
public function delete(int $taskId): void
{
Auth::user()->tasks()->findOrFail($taskId)->delete();
session()->flash('success', 'Task deleted successfully!');
}
public function updatingSearch(): void
{
$this->resetPage();
}
public function updatingFilterStatus(): void
{
$this->resetPage();
}
public function updatingFilterPriority(): void
{
$this->resetPage();
}
private function resetForm(): void
{
$this->editingTaskId = null;
$this->title = '';
$this->description = '';
$this->priority = 'medium';
$this->status = 'pending';
$this->due_date = '';
$this->resetValidation();
}
public function render()
{
$tasks = Auth::user()->tasks()
->when($this->search, fn ($q) => $q->where('title', 'like', '%' . $this->search . '%'))
->when($this->filterStatus, fn ($q) => $q->where('status', $this->filterStatus))
->when($this->filterPriority, fn ($q) => $q->where('priority', $this->filterPriority))
->latest()
->paginate(10);
return view('livewire.task-manager', compact('tasks'));
}
}
?>
8 Create Task Manager Views
<div class="max-w-5xl mx-auto">
{{-- Header --}}
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">My Tasks</h1>
<button wire:click="openCreateModal"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
+ New Task
</button>
</div>
{{-- Flash Messages --}}
@if (session('success'))
<div class="mb-4 p-3 bg-green-100 text-green-800 rounded-lg dark:bg-green-900 dark:text-green-200">
{{ session('success') }}
</div>
@endif
{{-- Search & Filters --}}
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Search tasks..."
class="w-full px-4 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 dark:text-white">
<select wire:model.live="filterStatus"
class="w-full px-4 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 dark:text-white">
<option value="">All Statuses</option>
<option value="pending">Pending</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
</select>
<select wire:model.live="filterPriority"
class="w-full px-4 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 dark:text-white">
<option value="">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
{{-- Task List --}}
<div class="space-y-3">
@forelse ($tasks as $task)
<div class="flex items-center justify-between p-4 bg-white rounded-lg shadow-sm border
dark:bg-gray-800 dark:border-gray-700
{{ $task->isOverdue() ? 'border-red-300 bg-red-50 dark:bg-red-900/20' : '' }}">
<div class="flex items-center gap-3 flex-1">
<button wire:click="toggleStatus({{ $task->id }})"
class="w-6 h-6 rounded-full border-2 flex items-center justify-center transition
{{ $task->status === 'completed'
? 'bg-green-500 border-green-500 text-white'
: 'border-gray-300 hover:border-blue-500' }}">
@if ($task->status === 'completed')
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
@endif
</button>
<div>
<h3 class="font-medium {{ $task->status === 'completed' ? 'line-through text-gray-400' : 'text-gray-900 dark:text-white' }}">
{{ $task->title }}
</h3>
<div class="flex items-center gap-2 mt-1 text-sm">
<span class="px-2 py-0.5 rounded text-xs font-medium
{{ $task->priority === 'high' ? 'bg-red-100 text-red-700' : '' }}
{{ $task->priority === 'medium' ? 'bg-yellow-100 text-yellow-700' : '' }}
{{ $task->priority === 'low' ? 'bg-green-100 text-green-700' : '' }}">
{{ ucfirst($task->priority) }}
</span>
@if ($task->due_date)
<span class="text-gray-500 dark:text-gray-400">
Due: {{ $task->due_date->format('M d, Y') }}
</span>
@endif
</div>
</div>
</div>
<div class="flex items-center gap-2">
<button wire:click="openEditModal({{ $task->id }})"
class="px-3 py-1 text-sm bg-gray-100 rounded hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 dark:text-white">
Edit
</button>
<button wire:click="delete({{ $task->id }})"
wire:confirm="Are you sure you want to delete this task?"
class="px-3 py-1 text-sm bg-red-100 text-red-700 rounded hover:bg-red-200">
Delete
</button>
</div>
</div>
@empty
<div class="text-center py-12 text-gray-500 dark:text-gray-400">
<p class="text-lg mb-2">No tasks found.</p>
<p>Click "+ New Task" to create your first task!</p>
</div>
@endforelse
</div>
{{-- Pagination --}}
<div class="mt-6">
{{ $tasks->links() }}
</div>
{{-- Create/Edit Modal --}}
@if ($showModal)
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-xl w-full max-w-lg mx-4 p-6">
<h2 class="text-xl font-bold mb-4 text-gray-900 dark:text-white">
{{ $editingTaskId ? 'Edit Task' : 'Create New Task' }}
</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Title *</label>
<input wire:model="title" type="text"
class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
@error('title') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<textarea wire:model="description" rows="3"
class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"></textarea>
@error('description') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Priority</label>
<select wire:model="priority"
class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Status</label>
<select wire:model="status"
class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="pending">Pending</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
</select>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Due Date</label>
<input wire:model="due_date" type="date"
class="w-full px-3 py-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
@error('due_date') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
</div>
</div>
<div class="flex justify-end gap-3 mt-6">
<button wire:click="$set('showModal', false)"
class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300">
Cancel
</button>
<button wire:click="save"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
{{ $editingTaskId ? 'Update Task' : 'Create Task' }}
</button>
</div>
</div>
</div>
@endif
</div>
9 Add Live Search & Filtering
1. wire:model.live.debounce.300ms — Real-time Search
The wire:model.live directive sends the search input value to the server on every keystroke. The debounce.300ms modifier adds a 300-millisecond delay so it does not fire on every single character:
<input wire:model.live.debounce.300ms="search" type="text" placeholder="Search tasks...">
2. wire:model.live — Instant Filter DropdownsDropdowns use wire:model.live without debounce, so the filter applies immediately on selection:
<select wire:model.live="filterStatus">
<option value="">All Statuses</option>
<option value="pending">Pending</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
</select>
3. Query Builder — Dynamic Filtering in render()The render() method chains conditional when() clauses to build the query dynamically:
$tasks = Auth::user()->tasks()
->when($this->search, fn ($q) =>
$q->where('title', 'like', '%' . $this->search . '%')
)
->when($this->filterStatus, fn ($q) =>
$q->where('status', $this->filterStatus)
)
->when($this->filterPriority, fn ($q) =>
$q->where('priority', $this->filterPriority)
)
->latest()
->paginate(10);
4. Resetting Pagination on Filter ChangeThe updatingSearch(), updatingFilterStatus(), and updatingFilterPriority() lifecycle hooks reset pagination to page 1 whenever a filter changes. Without this, you could end up on a page that no longer exists after filtering:
public function updatingSearch(): void
{
$this->resetPage();
}
All of this happens without a single page reload — Livewire handles the AJAX communication, DOM diffing, and re-rendering automatically behind the scenes.
10 Define Routes
<?php
use Illuminate\Support\Facades\Route;
use App\Livewire\TaskManager;
Route::get('/', function () {
return view('welcome');
})->name('home');
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
// Task Manager (Livewire full-page component)
Route::get('/tasks', TaskManager::class)->name('tasks');
});
require __DIR__ . '/auth.php';
Notice that we use TaskManager::class directly as the route action — Livewire 3 supports full-page components that can serve as the entire page response. The component will automatically use the layouts.app layout.To add a "Tasks" link in the navigation, open resources/views/components/layouts/app.blade.php and add this link alongside the existing Dashboard link:
<flux:navlist.item :href="route('tasks')" :current="request()->routeIs('tasks')" wire:navigate>
Tasks
</flux:navlist.item>
The wire:navigate attribute enables SPA-like navigation — pages load instantly without full page refreshes. 11 Customize the Dashboard
<x-layouts.app :title="__( 'Dashboard')">
<div class="max-w-5xl mx-auto">
<h1 class="text-2xl font-bold mb-6 text-gray-900 dark:text-white">
Welcome back, {{ auth()->user()->name }}!
</h1>
{{-- Stats Cards --}}
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<div class="bg-white dark:bg-gray-800 rounded-xl p-5 shadow-sm border dark:border-gray-700">
<p class="text-sm text-gray-500 dark:text-gray-400">Total Tasks</p>
<p class="text-3xl font-bold text-gray-900 dark:text-white">
{{ auth()->user()->tasks()->count() }}
</p>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl p-5 shadow-sm border dark:border-gray-700">
<p class="text-sm text-gray-500 dark:text-gray-400">Pending</p>
<p class="text-3xl font-bold text-yellow-600">
{{ auth()->user()->tasks()->where('status', 'pending')->count() }}
</p>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl p-5 shadow-sm border dark:border-gray-700">
<p class="text-sm text-gray-500 dark:text-gray-400">In Progress</p>
<p class="text-3xl font-bold text-blue-600">
{{ auth()->user()->tasks()->where('status', 'in_progress')->count() }}
</p>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl p-5 shadow-sm border dark:border-gray-700">
<p class="text-sm text-gray-500 dark:text-gray-400">Completed</p>
<p class="text-3xl font-bold text-green-600">
{{ auth()->user()->tasks()->where('status', 'completed')->count() }}
</p>
</div>
</div>
{{-- Quick Link --}}
<a href="{{ route('tasks') }}" wire:navigate
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition">
Go to Task Manager →
</a>
</div>
</x-layouts.app>
12 Folder Structure
13 Run and Test
# Terminal 1 - Vite dev server
npm run dev
# Terminal 2 - Laravel dev server
php artisan serve
Now test the following scenarios: 1. Register a new account
- Go to `http://localhost:8000/register`
- Fill in name, email, password
- → Redirected to the dashboard with your name displayed
2. View the Dashboard
- Go to `http://localhost:8000/dashboard`
- → Stats cards show 0 tasks across all statuses
3. Create a Task
- Navigate to Tasks via the sidebar
- Click "+ New Task"
- Fill in: Title = "Buy groceries", Priority = High, Due Date = tomorrow
- Click "Create Task" → Task appears in the list instantly (no page reload)
4. Test Live Search
- Create 3-4 more tasks
- Type in the search box → Results filter in real-time as you type
5. Test Filter Dropdowns
- Change status filter to "Pending" → Only pending tasks shown
- Change priority filter to "High" → Combined filtering works
6. Toggle Task Completion
- Click the circle button on any task → Toggles between pending and completed
- Completed tasks show strikethrough text and green checkmark
7. Edit a Task
- Click "Edit" on any task → Modal opens with pre-filled data
- Change the title and priority → Click "Update Task"
- → Changes reflected immediately
8. Delete a Task
- Click "Delete" → Browser confirmation dialog appears
- Confirm → Task removed from list with success message
9. Test Validation
- Try creating a task with an empty title → Error message appears
- Set a past due date → "Due date cannot be in the past" error
10. Test Dark Mode
- Go to Settings → Appearance
- Switch to dark mode → Entire app including task manager respects dark theme
11. Test SPA-like Navigation
- Click between Dashboard and Tasks in the sidebar
- → Pages switch instantly without full reloads (thanks to wire:navigate)
14 Conclusion
Here is a summary of what you learned:
- Installing the official Laravel 12 Livewire starter kit from the standalone repository
- Understanding the starter kit architecture: Volt single-file components, Flux UI, Tailwind CSS 4
- The difference between traditional Livewire 3 class components and Volt single-file components
- Building a complete Livewire CRUD component with create, read, update, and delete
- Implementing real-time live search with debounce and dynamic query building
- Using wire:model.live for instant filtering without page reloads
- Creating modals, flash messages, and inline status toggling in Livewire
- Customizing the default dashboard with dynamic data from Eloquent queries
- Using wire:navigate for SPA-like page transitions
- Dark mode support with Tailwind CSS dark: variants
Next Steps to Extend This Project:
- Add drag-and-drop task reordering using Livewire SortableJS
- Build a Kanban board view with columns for each status
- Add task categories or labels with Livewire multi-select
- Implement real-time notifications using Laravel Echo and Reverb
- Add file attachments to tasks using Livewire file uploads
- Build a team collaboration feature with task assignment
- Deploy to production with Laravel Forge or Vercel
The Livewire starter kit proves that you do not need React, Vue, or any JavaScript framework to build modern, interactive web applications. With Laravel 12, Livewire 3, and Volt, you get the best of both worlds — the simplicity of server-rendered PHP and the responsiveness of a single-page app.
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
It is an official first-party starter kit maintained by the Laravel team that scaffolds a new Laravel 12 project with Livewire 3, Volt, Tailwind CSS 4, and Flux UI. It includes pre-built authentication (login, register, password reset, email verification) and settings pages out of the box.
Livewire 3 uses separate PHP class files and Blade template files for each component. Volt is a layer on top of Livewire that allows you to write both the PHP logic and Blade template in a single .blade.php file using the anonymous class syntax. Both produce the same result — Volt is just a more concise syntax.
No. Livewire handles all the reactivity, AJAX requests, and DOM updates automatically on the server side using PHP. You write PHP and Blade — Livewire translates it into dynamic, reactive behavior in the browser without any custom JavaScript.
In Laravel 12, Breeze was replaced by standalone starter kit repositories. Instead of installing a package into an existing project, you create a new project directly from the starter kit repo using the laravel new command with the --using flag. The Livewire starter kit is one of the two official options (the other is React/Inertia).
Yes. The starter kit defaults to SQLite for simplicity, but you can easily switch to MySQL by updating the DB_CONNECTION, DB_DATABASE, DB_USERNAME, and DB_PASSWORD values in your .env file, then running php artisan migrate.
