Laravel Livewire 3 in 2026: Reactieve Applicaties en Sollicitatievragen

Laravel Livewire 3 beheersen met reactieve componenten, PHP 8 attributen, Alpine.js integratie en voorbereiding op technische interviews.

Laravel Livewire 3 in 2026: Reactieve Applicaties en Sollicitatievragen

Laravel Livewire 3 transformeert hoe PHP-ontwikkelaars reactieve, dynamische interfaces bouwen zonder JavaScript te schrijven. Als een belangrijke evolutie van het framework brengt Livewire 3 Alpine.js-integratie, verbeterde prestaties door morphing en een schonere componentsyntax die real-time applicaties native laat aanvoelen binnen het Laravel-ecosysteem.

Livewire 3 Kernwijziging

Livewire 3 gebruikt intern Alpine.js en vereist de @livewireStyles en @livewireScripts directives in de layout. De wire:model directive gebruikt standaard lazy binding—gebruik wire:model.live voor real-time updates.

Livewire 3 Architectuur en Reactieve Binding Begrijpen

Livewire werkt via een eenvoudig maar krachtig concept: PHP-componenten die op de server opnieuw worden gerenderd en HTML-verschillen terugsturen naar de browser. Elk component behoudt zijn state tussen requests via een serialisatiemechanisme dat property-waarden bewaart.

De architectuur verschilt fundamenteel van JavaScript-gebaseerde reactiviteit. In plaats van een virtuele DOM bij te houden op de client, serialiseert Livewire de componentstate, stuurt deze mee met elk request en transformeert de DOM op basis van de serverresponse. Deze aanpak houdt de businesslogica in PHP terwijl het de reactieve ervaring levert die gebruikers verwachten.

app/Livewire/SearchUsers.phpphp
<?php

namespace App\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\WithPagination;
use Livewire\Attributes\Url;

class SearchUsers extends Component
{
    use WithPagination;

    #[Url] // Synchroniseert property met URL query string
    public string $search = '';

    #[Url]
    public string $role = '';

    public function updatedSearch(): void
    {
        // Reset paginering wanneer zoekopdracht wijzigt
        $this->resetPage();
    }

    public function render()
    {
        return view('livewire.search-users', [
            'users' => User::query()
                ->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
                ->when($this->role, fn($q) => $q->where('role', $this->role))
                ->paginate(10),
        ]);
    }
}

Het #[Url] attribuut synchroniseert properties automatisch met URL query parameters, waardoor deelbare gefilterde weergaven mogelijk zijn zonder extra code. De updatedSearch lifecycle hook wordt getriggerd wanneer de search property wijzigt en reset de paginering naar pagina één.

Livewire 3 Componentsyntax met PHP 8 Attributen

Livewire 3 omarmt PHP 8 attributen voor configuratie en vervangt de eerdere methode-gebaseerde aanpak. Deze declaratieve stijl verbetert de leesbaarheid en vermindert boilerplate code in componenten.

app/Livewire/CreatePost.phpphp
<?php

namespace App\Livewire;

use App\Models\Post;
use Livewire\Component;
use Livewire\Attributes\Rule;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Computed;

class CreatePost extends Component
{
    #[Locked] // Kan niet worden gewijzigd vanuit de frontend
    public int $userId;

    #[Rule('required|min:5|max:200')]
    public string $title = '';

    #[Rule('required|min:50')]
    public string $content = '';

    #[Rule('nullable|array')]
    public array $tags = [];

    public function mount(): void
    {
        $this->userId = auth()->id();
    }

    #[Computed]
    public function wordCount(): int
    {
        return str_word_count($this->content);
    }

    public function save(): void
    {
        $validated = $this->validate();

        Post::create([
            'user_id' => $this->userId,
            ...$validated,
        ]);

        $this->reset(['title', 'content', 'tags']);
        $this->dispatch('post-created');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}

Het #[Locked] attribuut voorkomt frontend-manipulatie van gevoelige properties—cruciaal voor beveiliging bij het doorgeven van ID's of permissies. #[Rule] attributen definiëren validatie inline, terwijl #[Computed] dure berekeningen cachet totdat dependencies wijzigen.

Beveiligingsoverweging

Gebruik altijd #[Locked] voor gebruikers-ID's, permissie-flags en alle data die niet vanuit de browser mag worden gemanipuleerd. Livewire verzendt de componentstate bij elk request, waardoor onbeschermde properties kwetsbaar zijn voor wijziging.

Real-time Formuliervalidatie en Gebruikersfeedback

Livewire blinkt uit in het bieden van directe validatiefeedback zonder pagina-herlading. Het framework valideert properties terwijl gebruikers typen en toont fouten in real-time naast formuliervelden.

app/Livewire/RegistrationForm.phpphp
<?php

namespace App\Livewire;

use App\Models\User;
use Livewire\Component;
use Livewire\Attributes\Rule;
use Livewire\Attributes\Validate;
use Illuminate\Validation\Rules\Password;

class RegistrationForm extends Component
{
    #[Rule('required|email|unique:users,email')]
    public string $email = '';

    #[Rule('required|min:2|max:50')]
    public string $name = '';

    public string $password = '';
    public string $passwordConfirmation = '';

    public function rules(): array
    {
        return [
            'password' => [
                'required',
                'confirmed',
                Password::min(8)->mixedCase()->numbers(),
            ],
        ];
    }

    public function updated(string $property): void
    {
        // Valideer enkele property bij wijziging
        $this->validateOnly($property);
    }

    public function register(): void
    {
        $validated = $this->validate();

        $user = User::create([
            'email' => $validated['email'],
            'name' => $validated['name'],
            'password' => bcrypt($validated['password']),
        ]);

        auth()->login($user);
        $this->redirect('/dashboard');
    }

    public function render()
    {
        return view('livewire.registration-form');
    }
}

Het bijbehorende Blade-template gebruikt wire:model.blur om validatie te triggeren wanneer gebruikers een veld verlaten, waarbij responsiviteit wordt afgewogen tegen serverbelasting:

blade
{{-- resources/views/livewire/registration-form.blade.php --}}
<form wire:submit="register" class="space-y-4">
    <div>
        <label for="email" class="block text-sm font-medium">E-mail</label>
        <input 
            wire:model.blur="email" 
            type="email" 
            id="email"
            class="mt-1 block w-full rounded-md border-gray-300"
        >
        @error('email')
            <p class="mt-1 text-sm text-red-600">{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label for="password" class="block text-sm font-medium">Wachtwoord</label>
        <input 
            wire:model.blur="password" 
            type="password" 
            id="password"
            class="mt-1 block w-full rounded-md border-gray-300"
        >
        @error('password')
            <p class="mt-1 text-sm text-red-600">{{ $message }}</p>
        @enderror
    </div>

    <div>
        <label for="passwordConfirmation" class="block text-sm font-medium">Bevestig Wachtwoord</label>
        <input 
            wire:model.blur="passwordConfirmation" 
            type="password" 
            id="passwordConfirmation"
            class="mt-1 block w-full rounded-md border-gray-300"
        >
    </div>

    <button 
        type="submit" 
        class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md"
        wire:loading.attr="disabled"
    >
        <span wire:loading.remove>Registreren</span>
        <span wire:loading>Verwerken...</span>
    </button>
</form>

Dit patroon demonstreert de sterke punten van Livewire: server-side validatielogica met client-side responsiviteit, laadstatussen zonder JavaScript en duidelijke scheiding tussen componentlogica en presentatie.

Ouder-Kind Componentcommunicatie met Events

Complexe interfaces vereisen vaak meerdere Livewire-componenten die met elkaar communiceren. Livewire 3 biedt een verfijnd eventsysteem voor dit doel, gebruikmakend van het dispatch en #[On] attribuut patroon.

app/Livewire/TaskBoard.php - Oudercomponentphp
<?php

namespace App\Livewire;

use App\Models\Task;
use Livewire\Component;
use Livewire\Attributes\On;

class TaskBoard extends Component
{
    public array $columns = ['todo', 'in_progress', 'done'];

    #[On('task-moved')]
    public function handleTaskMoved(int $taskId, string $newStatus): void
    {
        Task::where('id', $taskId)->update(['status' => $newStatus]);
    }

    #[On('task-created')]
    public function refreshBoard(): void
    {
        // Component wordt automatisch opnieuw gerenderd
    }

    public function render()
    {
        return view('livewire.task-board', [
            'tasks' => Task::all()->groupBy('status'),
        ]);
    }
}
app/Livewire/TaskCard.php - Kindcomponentphp
<?php

namespace App\Livewire;

use App\Models\Task;
use Livewire\Component;

class TaskCard extends Component
{
    public Task $task;

    public function moveToStatus(string $status): void
    {
        $this->dispatch('task-moved', taskId: $this->task->id, newStatus: $status);
    }

    public function render()
    {
        return view('livewire.task-card');
    }
}

Events bubbelen standaard omhoog door de componenthiërarchie. Voor cross-component communicatie buiten de ouder-kind relatie kunnen events specifieke componenten targeten met $this->dispatch('event-name')->to(ComponentClass::class).

Klaar om je Laravel gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Alpine.js Integratie voor Client-side Interactiviteit

Livewire 3 wordt geleverd met Alpine.js ingebouwd, wat hybride patronen mogelijk maakt waarbij sommige interacties client-side plaatsvinden terwijl andere naar de server gaan. Deze combinatie vermindert serverbelasting voor eenvoudige UI-toggles terwijl Livewire's server-side model behouden blijft voor complexe logica.

blade
{{-- resources/views/livewire/notification-dropdown.blade.php --}}
<div 
    x-data="{ 
        open: false,
        unreadCount: @entangle('unreadCount')
    }"
    class="relative"
>
    <button @click="open = !open" class="relative p-2">
        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 
                  d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6 6 0 10-12 0v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
        </svg>
        <span 
            x-show="unreadCount > 0"
            x-text="unreadCount"
            class="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"
        ></span>
    </button>

    <div 
        x-show="open" 
        @click.outside="open = false"
        x-transition
        class="absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg"
    >
        @foreach($notifications as $notification)
            <div 
                wire:click="markAsRead({{ $notification->id }})"
                class="p-4 border-b hover:bg-gray-50 cursor-pointer"
            >
                <p class="font-medium">{{ $notification->title }}</p>
                <p class="text-sm text-gray-600">{{ $notification->message }}</p>
            </div>
        @endforeach
    </div>
</div>

De @entangle directive creëert een tweerichtingsbinding tussen Alpine.js state en Livewire properties. Wijzigingen aan unreadCount in Alpine of Livewire synchroniseren automatisch met de andere, wat optimistische UI-updates mogelijk maakt terwijl serverautoriteit behouden blijft.

Bestandsuploads met Voortgangsmonitoring

Livewire biedt bestandsupload-handling met ingebouwde voortgangsmonitoring, validatie en tijdelijke opslag. De WithFileUploads trait voegt alle benodigde functionaliteit toe aan elk component.

app/Livewire/DocumentUploader.phpphp
<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Rule;
use Illuminate\Support\Facades\Storage;

class DocumentUploader extends Component
{
    use WithFileUploads;

    #[Rule('required|file|mimes:pdf,docx|max:10240')] // 10MB max
    public $document;

    public ?string $uploadedPath = null;

    public function updatedDocument(): void
    {
        $this->validate();
    }

    public function save(): void
    {
        $this->validate();

        $this->uploadedPath = $this->document->store('documents', 'public');

        $this->dispatch('document-uploaded', path: $this->uploadedPath);
        $this->reset('document');
    }

    public function render()
    {
        return view('livewire.document-uploader');
    }
}
blade
{{-- resources/views/livewire/document-uploader.blade.php --}}
<div class="space-y-4">
    <div 
        x-data="{ uploading: false, progress: 0 }"
        x-on:livewire-upload-start="uploading = true"
        x-on:livewire-upload-finish="uploading = false; progress = 0"
        x-on:livewire-upload-error="uploading = false"
        x-on:livewire-upload-progress="progress = $event.detail.progress"
    >
        <input 
            type="file" 
            wire:model="document"
            accept=".pdf,.docx"
            class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:bg-indigo-50 file:text-indigo-700"
        >

        <div x-show="uploading" class="mt-2">
            <div class="bg-gray-200 rounded-full h-2">
                <div 
                    class="bg-indigo-600 h-2 rounded-full transition-all duration-300"
                    :style="'width: ' + progress + '%'"
                ></div>
            </div>
            <p class="text-sm text-gray-600 mt-1" x-text="'Uploaden: ' + progress + '%'"></p>
        </div>
    </div>

    @error('document')
        <p class="text-sm text-red-600">{{ $message }}</p>
    @enderror

    @if($document)
        <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
            <span class="text-sm">{{ $document->getClientOriginalName() }}</span>
            <button wire:click="save" class="bg-indigo-600 text-white px-4 py-2 rounded-md text-sm">
                Uploaden
            </button>
        </div>
    @endif
</div>

Het template combineert Alpine.js voor client-side voortgangsvisualisatie met Livewire's upload events. Bestanden worden eerst naar tijdelijke opslag geüpload, wat preview en validatie mogelijk maakt voor definitieve opslag.

Sollicitatievragen voor Livewire 3 Ontwikkelaars

Technische interviews onderzoeken vaak het begrip van Livewire's architectuur, beveiligingsmodel en prestatiekenmerken. De volgende vragen komen frequent voor bij het aannemen van Laravel-ontwikkelaars met Livewire-ervaring.

Interview Focusgebieden

Interviewers beoordelen drie hoofdgebieden: begrip van Livewire's request lifecycle, beveiligingsbewustzijn rondom publieke properties en het vermogen om componentprestaties te optimaliseren. Aantonen wanneer Livewire NIET te gebruiken is net zo belangrijk als weten hoe het te gebruiken.

Hoe behoudt Livewire componentstate tussen requests?

Livewire serialiseert alle publieke properties in een payload die meereist met elk request. De state van het component wordt gecodeerd, gesigneerd tegen manipulatie en naar de browser gestuurd. Bij volgende interacties keert deze payload terug naar de server waar Livewire de componentinstantie reconstrueert voordat de actie wordt verwerkt.

Welke beveiligingszorgen bestaan er bij Livewire publieke properties en hoe adresseert Livewire deze?

Publieke properties zijn kwetsbaar omdat ze zijn opgenomen in de geserialiseerde payload die naar de browser wordt gestuurd. Kwaadwillende gebruikers zouden deze payload kunnen wijzigen voordat ze deze terugsturen. Livewire adresseert dit via checksum-verificatie—elke manipulatie veroorzaakt het falen van het request. Daarnaast markeert het #[Locked] attribuut properties als server-only, waardoor wijziging via de frontend wordt voorkomen zelfs als checksums zouden zijn gecompromitteerd.

Leg het verschil uit tussen wire:model, wire:model.live en wire:model.blur.

In Livewire 3 gebruikt wire:model standaard uitgestelde binding—de property wordt alleen geüpdatet wanneer een actie triggert (zoals formulier inzending). wire:model.live updatet de property bij elke input-wijziging en triggert een server roundtrip bij elke toetsaanslag. wire:model.blur updatet wanneer het element focus verliest, waarbij responsiviteit wordt afgewogen tegen verminderde serverbelasting. De .debounce modifier voegt vertraging toe aan live updates: wire:model.live.debounce.500ms.

Wanneer zou een ontwikkelaar Livewire kiezen boven Inertia.js of een JavaScript-framework?

Livewire past het beste wanneer het team sterke PHP-expertise heeft maar beperkte JavaScript-ervaring, wanneer SEO-vereisten server-gerenderde content bevoordelen, of wanneer de applicatie complexe formulieren met server-side validatie omvat. Inertia.js of JavaScript-frameworks passen beter bij zeer interactieve dashboards met complexe client-side state, applicaties die offline functionaliteit vereisen, of teams met JavaScript-expertise die het SPA mentale model prefereren.

Hoe optimaliseer je Livewire-componenten voor prestaties?

Belangrijke optimalisaties omvatten: gebruik van wire:model.blur in plaats van wire:model.live om requests te verminderen, implementatie van #[Computed] voor dure berekeningen om caching mogelijk te maken, lazy loading van componenten met wire:init voor initiële render prestaties, gebruik van paginatie om data per request te limiteren, en vermijding van onnodige re-renders door reactieve properties te scopen. Voor lijsten helpt het wire:key attribuut Livewire om items efficiënt te tracken tijdens morphing.

Voor diepere verkenning van Laravel's event systeem samen met Livewire, zie de events en listeners sollicitatievragen. Het begrijpen van dependency injection patterns blijkt ook waardevol bij het structureren van complexe Livewire-componenten.

Livewire-componenten Effectief Testen

Livewire biedt een fluent test-API die integreert met Laravel's testframework. Tests kunnen gebruikersinteracties simuleren, componentstate asserteren en DOM-output verifiëren.

tests/Feature/Livewire/SearchUsersTest.phpphp
<?php

namespace Tests\Feature\Livewire;

use App\Livewire\SearchUsers;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase;

class SearchUsersTest extends TestCase
{
    use RefreshDatabase;

    public function test_can_search_users_by_name(): void
    {
        User::factory()->create(['name' => 'John Doe', 'role' => 'admin']);
        User::factory()->create(['name' => 'Jane Smith', 'role' => 'user']);

        Livewire::test(SearchUsers::class)
            ->set('search', 'John')
            ->assertSee('John Doe')
            ->assertDontSee('Jane Smith');
    }

    public function test_can_filter_by_role(): void
    {
        User::factory()->create(['name' => 'Admin User', 'role' => 'admin']);
        User::factory()->create(['name' => 'Regular User', 'role' => 'user']);

        Livewire::test(SearchUsers::class)
            ->set('role', 'admin')
            ->assertSee('Admin User')
            ->assertDontSee('Regular User');
    }

    public function test_pagination_resets_on_search_change(): void
    {
        User::factory()->count(25)->create();

        Livewire::test(SearchUsers::class)
            ->call('gotoPage', 2)
            ->assertSet('paginators.page', 2)
            ->set('search', 'test')
            ->assertSet('paginators.page', 1);
    }

    public function test_url_reflects_search_parameters(): void
    {
        Livewire::withQueryParams(['search' => 'existing'])
            ->test(SearchUsers::class)
            ->assertSet('search', 'existing');
    }
}

De testsuite dekt functionaliteit, state management en URL-synchronisatie. Voor uitgebreide Laravel-teststrategieën inclusief Livewire-componenten biedt het artikel Laravel Testing Best Practices aanvullende patronen en technieken.

Conclusie

  • Livewire 3 maakt reactieve interfaces mogelijk in pure PHP via server-side rendering en DOM morphing, waardoor aparte JavaScript-frameworks in veel Laravel-applicaties overbodig worden
  • PHP 8 attributen (#[Rule], #[Locked], #[Computed], #[Url]) bieden declaratieve configuratie die code-leesbaarheid en beveiliging verbetert
  • De meegeleverde Alpine.js-integratie maakt hybride patronen mogelijk waarbij eenvoudige interacties client-side blijven terwijl complexe logica op de server blijft
  • Beveiliging vereist aandacht: gebruik #[Locked] voor gevoelige properties en begrijp dat publieke properties meereizen in elke request payload
  • Prestatie-optimalisatie concentreert zich op het verminderen van roundtrips via geschikte wire:model modifiers en implementatie van computed properties voor dure operaties
  • Sollicitatievragen focussen op architectuurbegrip, beveiligingsbewustzijn en weten wanneer alternatieve benaderingen (Inertia.js, SPAs) beter bij de vereisten passen

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Delen

Gerelateerde artikelen