Livewire 3: navigation, forms, and a much bigger ambition

Understand Livewire 3’s form objects, deferred binding, and navigation lifecycle through a server-authorized form and concrete migration checks.
The historical release and the example boundary
Livewire 3.0.0 was released on 24 August 2023. Its form objects and navigation support expanded what Laravel teams could build with server-rendered components. The useful architectural question is where state, validation, and authorization live when interactions no longer reload an entire page.
This article describes the v3 generation, not the newest Livewire major. The PHP example targets Livewire 3 with Laravel 10 and PHP 8.1 or newer. It assumes an existing authenticated page, a Post model with title, body, and user_id columns, and a registered PostPolicy whose create method permits the intended users. It also requires the Blade view described below; the two classes are not a complete application.
Keep form fields together
A form object gives related fields and rules one home. The rules method below performs validation when explicitly requested; it does not promise live feedback on every keystroke. That makes the submit boundary visible and avoids depending on later attribute naming in v3 documentation.
<?php
namespace App\Livewire\Forms;
use Livewire\Form;
class PostForm extends Form
{
public string $title = '';
public string $body = '';
protected function rules(): array
{
return [
'title' => ['required', 'string', 'max:160'],
'body' => ['required', 'string', 'max:20000'],
];
}
}
Authorize the action and preserve failed input
The component checks authorization on the server before saving. Hiding a submit control is insufficient because public actions can be invoked independently of the visible UI. Assigning user_id from the authenticated session prevents the browser from choosing an author.
The query failure path reports the exception through Laravel and leaves the form populated. Production logging must redact sensitive bindings according to the application’s logging policy. Validation and authorization exceptions retain their normal framework behavior; neither is converted into a false success message.
<?php
namespace App\Livewire;
use App\Livewire\Forms\PostForm;
use App\Models\Post;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Gate;
use Livewire\Component;
class CreatePost extends Component
{
public PostForm $form;
public function save(): void
{
abort_unless(auth()->check(), 401);
Gate::authorize('create', Post::class);
$data = $this->form->validate();
try {
$post = new Post();
$post->title = $data['title'];
$post->body = $data['body'];
$post->user_id = auth()->id();
$post->save();
} catch (QueryException $exception) {
report($exception);
$this->addError('form.title', 'Save failed. Your text is still here.');
return;
}
$this->form->reset();
session()->flash('status', 'Post created.');
}
public function render()
{
return view('livewire.create-post');
}
}
Wire the view to the submit boundary
In resources/views/livewire/create-post.blade.php, use one root element containing a form with wire:submit="save". Bind the title input to wire:model="form.title" and the body textarea to wire:model="form.body". Render escaped field errors for both paths and the escaped session status. Include associated labels and a submit button.
Livewire 3 defers ordinary wire:model updates until a request such as submission. During a v2 migration, use wire:model.live where the old immediate-update behavior is actually needed. Do not mechanically add it to every field: request frequency and validation timing should follow the interaction. The form above validates on submit.
Navigation changes JavaScript lifecycle assumptions
Adding wire:navigate to an internal link lets Livewire fetch and swap page content without a conventional full reload. Initialization code that only listens for DOMContentLoaded will therefore miss later visits. The v3 navigation documentation provides livewire:navigated for initial load and completed navigations.
Document-level listeners survive navigation. Registering them again on every visit can duplicate analytics events or actions. Make widget initialization repeatable and remove listeners when their owning UI is replaced. Check back and forward navigation, direct page loads, and a visit away from and back to the form.
Finally, a disabled submit button is not a database-level duplicate-write guarantee. For workflows where repeated submission has a material consequence, define an application idempotency strategy. This sample creates a simple post and intentionally does not claim exactly-once creation under retries.

