Kritim Yantra
May 12, 2025
Laravel 12, released in early 2025, continues to push the boundaries of elegant web development. One of its most exciting features is the seamless integration with Livewire, a powerful package that lets you build interactive interfaces using just PHP and Blade—no JavaScript required.
This blog is your step-by-step guide to building your first Livewire application using Laravel 12. We'll cover everything from installation and configuration to creating components, handling forms, real-time validation, CRUD operations, file uploads, and even pagination.
Before diving in, make sure your development environment is ready:
Start by creating a fresh Laravel project:
composer global require laravel/installer
laravel new laravel12-livewire-tutorial
cd laravel12-livewire-tutorial
Set your database credentials in the .env
file and run:
php artisan key:generate
Add Livewire to your project with Composer:
composer require livewire/livewire
Then publish its config and asset files (optional):
php artisan livewire:publish --assets
php artisan vendor:publish --tag=livewire:config
Update your layout (usually resources/views/layouts/app.blade.php
) to include Livewire styles and scripts:
<!-- Inside <head> -->
@livewireStyles
<!-- Before </body> -->
@livewireScripts
Laravel 12 offers a Livewire UI starter kit:
php artisan ui livewire
npm install && npm run dev
php artisan migrate
This scaffolds basic auth and dashboard views for rapid prototyping.
Generate a simple counter component:
php artisan make:livewire Counter
Edit app/Http/Livewire/Counter.php
:
namespace App\Http\Livewire;
use Livewire\Component;
class Counter extends Component
{
public $count = 0;
public function increment()
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}
Edit the view resources/views/livewire/counter.blade.php
:
<div style="text-align: center;">
<button wire:click="increment">+</button>
<h1>{{ $count }}</h1>
</div>
Display it in your page:
@livewire('counter')
Update Counter.php
with validation logic:
public $name = '';
protected $rules = [
'name' => 'required|min:3',
];
public function updated($propertyName)
{
$this->validateOnly($propertyName);
}
And update the view:
<input type="text" wire:model="name">
@error('name') <span class="error">{{ $message }}</span> @enderror
php artisan make:model Task -m
Update migration:
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->timestamps();
});
Run migration:
php artisan migrate
php artisan make:livewire Tasks
Tasks.php
public $tasks, $title, $description, $task_id;
public $updateMode = false;
public function render()
{
$this->tasks = Task::all();
return view('livewire.tasks');
}
private function resetInputFields()
{
$this->title = '';
$this->description = '';
}
public function store()
{
$validated = $this->validate([
'title' => 'required|min:3',
'description' => 'nullable',
]);
Task::create($validated);
session()->flash('message', 'Task Created Successfully.');
$this->resetInputFields();
}
public function edit($id)
{
$task = Task::findOrFail($id);
$this->task_id = $id;
$this->title = $task->title;
$this->description = $task->description;
$this->updateMode = true;
}
public function update()
{
$validated = $this->validate([
'title' => 'required|min:3',
'description' => 'nullable',
]);
$task = Task::find($this->task_id);
$task->update($validated);
$this->updateMode = false;
session()->flash('message', 'Task Updated Successfully.');
$this->resetInputFields();
}
public function delete($id)
{
Task::find($id)->delete();
session()->flash('message', 'Task Deleted Successfully.');
}
tasks.blade.php
Add form inputs, buttons, and a list of tasks with wire:model
, wire:click
, and conditional rendering.
Use the WithFileUploads
trait:
use Livewire\WithFileUploads;
class Tasks extends Component
{
use WithFileUploads;
public $photo;
// ...
}
Handle file uploads:
$this->validate([
'photo' => 'image|max:1024',
]);
$path = $this->photo->store('photos', 'public');
Update Blade:
<input type="file" wire:model="photo">
@error('photo') <span class="error">{{ $message }}</span> @enderror
Use the WithPagination
trait:
use Livewire\WithPagination;
class Tasks extends Component
{
use WithPagination;
protected $paginationTheme = 'tailwind';
}
Render paginated tasks:
public function render()
{
return view('livewire.tasks', [
'tasks' => Task::paginate(10),
]);
}
In Blade:
{{ $tasks->links() }}
Compile assets:
npm run production
Set file permissions:
chmod -R 775 storage bootstrap/cache
Optimize:
php artisan config:cache
php artisan route:cache
php artisan view:cache
You’ve just built a dynamic web application using Laravel 12 and Livewire! This stack allows you to develop reactive interfaces using only PHP and Blade, avoiding the complexity of JavaScript-heavy frameworks. From simple counters to full CRUD with validation, file uploads, and pagination, you're now equipped to create production-ready apps.
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google