Livewire 4 : le guide complet des nouveautés qui révolutionnent vos composants Laravel

L
Laravel Actu
6 min

Livewire 4 marque un tournant majeur pour le développement d’interfaces réactives en Laravel. Cette version, publiée le 14 janvier 2026 par Caleb Porzio, repense entièrement la façon d’écrire des composants : tout peut désormais tenir dans un seul fichier, avec du PHP, du Blade, du CSS scopé et du JavaScript contextuel.

Qu’est-ce que Livewire et pourquoi l’utiliser ?

Livewire permet de créer des interfaces dynamiques en PHP pur, sans écrire une ligne de JavaScript. Chaque composant Livewire communique avec le serveur via AJAX de manière transparente : vous modifiez une propriété PHP, et le DOM se met à jour automatiquement.

Les cas d’usage typiques :

  • Formulaires avec validation temps réel
  • Tableaux avec tri, filtres et pagination
  • Modales interactives
  • Dashboards avec données live

Avant Livewire 4, il fallait jongler entre une classe PHP et un fichier Blade séparé. Cette époque est révolue.

Installation de Livewire 4

Mettez à jour votre projet Laravel 12+ vers Livewire 4 :

# Installation ou mise à jour vers Livewire 4
composer require livewire/livewire:^4.0

# Vider le cache après mise à jour
php artisan optimize:clear

Pour un nouveau projet Laravel, le starter kit Livewire intègre directement la v4 :

# Créer un projet avec le starter kit Livewire
laravel new mon-projet --livewire

Single-File Components : tout dans un seul fichier

La révolution de Livewire 4, c’est le format single-file. Votre composant entier tient dans un fichier .blade.php préfixé d’un emoji ⚡ pour le distinguer des composants Blade classiques.

<?php
// resources/views/components/⚡compteur.blade.php

use Livewire\Component;

new class extends Component {
    // Propriété réactive accessible dans le template
    public int $compteur = 0;

    // Action déclenchée par wire:click
    public function incrementer(): void
    {
        $this->compteur++;
    }
};
?>

<div>
    <h2 class="titre">{{ $compteur }}</h2>
    <button wire:click="incrementer">+1</button>
</div>

<style>
    /* CSS scopé automatiquement au composant */
    .titre {
        font-size: 3rem;
        color: #3b82f6;
    }
</style>

<script>
    // JavaScript avec accès direct à $wire via 'this'
    this.$watch('compteur', (value) => {
        if (value >= 10) {
            console.log('Objectif atteint !')
        }
    })
</script>

Ce composant combine PHP, HTML, CSS scopé et JavaScript contextuel. Le CSS .titre ne fuite pas vers les autres composants. Le JavaScript accède au composant via this (alias de $wire).

Pour créer un nouveau composant :

# Crée un single-file component par défaut
php artisan make:livewire compteur

# Crée un multi-file component (dossier dédié)
php artisan make:livewire compteur --mfc

Islands : isolation intelligente des mises à jour

Les islands représentent LA fonctionnalité phare de Livewire 4. Elles créent des régions isolées qui se rafraîchissent indépendamment du reste du composant.

<?php
// resources/views/components/⚡dashboard.blade.php

use Livewire\Component;
use Livewire\Attributes\Computed;
use App\Models\Order;
use App\Models\User;

new class extends Component {
    // Computed property : exécuté uniquement quand l'island se rafraîchit
    #[Computed]
    public function chiffreAffaires(): float
    {
        return Order::whereMonth('created_at', now()->month)->sum('total');
    }

    #[Computed]
    public function nouveauxClients(): int
    {
        return User::whereDate('created_at', '>=', now()->subDays(7))->count();
    }
};
?>

<div class="grid grid-cols-2 gap-4">
    {{-- Cette island se rafraîchit seule sans toucher au reste --}}
    @island(name: 'revenue')
        <div class="p-4 bg-white rounded shadow">

<h3>Chiffre d'affaires du mois</h3>
            <p class="text-2xl font-bold">{{ number_format($this->chiffreAffaires, 2) }} €</p>
            <button wire:click="$refresh">Actualiser</button>
        </div>
    @endisland

    {{-- Island lazy : chargée après le rendu initial --}}
    @island(name: 'clients', lazy: true)
        <div class="p-4 bg-white rounded shadow">

<h3>Nouveaux clients (7 jours)</h3>
            <p class="text-2xl font-bold">{{ $this->nouveauxClients }}</p>
        </div>
    @endisland
</div>

Quand vous cliquez sur « Actualiser », seule l’island revenue fait une requête serveur et exécute sa computed property. L’island clients reste intacte. Auparavant, il fallait créer des composants enfants séparés pour obtenir cette isolation.

Slots : composition de composants réactifs

Livewire 4 supporte enfin les slots comme les composants Blade. Le contenu injecté reste réactif dans le contexte du parent.

<?php
// resources/views/components/⚡carte.blade.php

use Livewire\Component;

new class extends Component {
    public string $titre = '';
};
?>

<div class="border rounded-lg overflow-hidden">
    <header class="bg-gray-100 p-4">

<h3>{{ $titre }}</h3>
    </header>
    <div class="p-4">
        {{-- Le slot par défaut --}}
        {{ $slot }}
    </div>
    @isset($footer)
        <footer class="bg-gray-50 p-4">
            {{ $footer }}
        </footer>
    @endisset
</div>

Utilisation avec des slots nommés :

<?php
// resources/views/components/⚡liste-produits.blade.php

use Livewire\Component;
use App\Models\Product;

new class extends Component {
    public function supprimer(int $id): void
    {
        Product::destroy($id);
    }
};
?>

<div>
    @foreach(Product::all() as $produit)
        {{-- Le wire:click appelle la méthode du PARENT --}}
        <livewire:carte :titre="$produit->nom">

<p>{{ $produit->description }}</p>
            <p class="font-bold">{{ $produit->prix }} €</p>

            <x-slot:footer>
                <button wire:click="supprimer({{ $produit->id }})" class="text-red-600">
                    Supprimer
                </button>
            </x-slot:footer>
        </livewire:carte>
    @endforeach
</div>

Le bouton « Supprimer » appelle supprimer() sur le composant parent liste-produits, pas sur carte.

UI Optimiste : réactivité instantanée

Livewire 4 introduit plusieurs directives pour des interfaces qui réagissent instantanément, sans attendre le serveur.

<?php
// resources/views/components/⚡editeur-article.blade.php

use Livewire\Component;
use App\Models\Post;

new class extends Component {
    public Post $article;
    public string $titre = '';
    public string $contenu = '';

    public function mount(Post $article): void
    {
        $this->article = $article;
        $this->titre = $article->titre;
        $this->contenu = $article->contenu;
    }

    public function sauvegarder(): void
    {
        $this->article->update([
            'titre' => $this->titre,
            'contenu' => $this->contenu,
        ]);
    }
};
?>

<div>
    {{-- wire:show affiche/masque via CSS, sans requête serveur --}}
    <div wire:show="$dirty" class="bg-yellow-100 p-2 rounded mb-4">
        ⚠️ Modifications non sauvegardées
    </div>

    <input 
        wire:model="titre" 
        type="text" 
        class="w-full p-2 border rounded"
        {{-- wire:bind applique une classe dynamiquement côté client --}}
        wire:bind:class="titre.length > 100 && 'border-red-500'"
    >

    {{-- wire:text met à jour le texte instantanément --}}
    <p class="text-sm text-gray-500">
        <span wire:text="titre.length">0</span> / 100 caractères
    </p>

    <textarea wire:model="contenu" rows="10" class="w-full p-2 border rounded mt-4"></textarea>

    <button 
        wire:click="sauvegarder" 
        class="mt-4 px-4 py-2 bg-blue-600 text-white rounded data-loading:opacity-50"
    >
        Sauvegarder
    </button>
</div>

Les directives clés :

  • wire:show : toggle CSS instantané (pas de manipulation DOM)
  • wire:text : mise à jour de texte côté client
  • wire:bind : liaison d’attributs HTML dynamique
  • $dirty : détecte les modifications non sauvegardées
  • data-loading : attribut automatique pendant les requêtes

Drag & Drop natif avec wire:sort

Livewire 4 intègre le tri par glisser-déposer sans librairie externe.

<?php
// resources/views/components/⚡liste-taches.blade.php

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

new class extends Component {
    public function reordonner(int $taskId, int $nouvellePosition): void
    {
        $tache = Task::find($taskId);

        // Réorganise les positions des autres tâches
        Task::where('position', '>=', $nouvellePosition)
            ->where('id', '!=', $taskId)
            ->increment('position');

        $tache->update(['position' => $nouvellePosition]);
    }
};
?>

<ul wire:sort="reordonner" class="space-y-2">
    @foreach(Task::orderBy('position')->get() as $tache)
        <li 
            wire:key="{{ $tache->id }}" 
            wire:sort:item="{{ $tache->id }}"
            class="p-4 bg-white rounded shadow flex items-center gap-4"
        >
            {{-- Handle de drag explicite --}}
            <span wire:sort:handle class="cursor-grab">⠿</span>

            <span>{{ $tache->titre }}</span>

            {{-- wire:sort:ignore empêche le drag sur cet élément --}}
            <button wire:sort:ignore wire:click="supprimer({{ $tache->id }})">
                🗑️
            </button>
        </li>
    @endforeach
</ul>

La méthode reordonner() reçoit l’ID de l’élément déplacé et sa nouvelle position. Les animations de transition sont gérées automatiquement.

Routing unifié avec Route::livewire()

Livewire 4 introduit une nouvelle macro de routing cohérente :

// routes/web.php
use Illuminate\Support\Facades\Route;

// Nouvelle syntaxe recommandée : référence par nom
Route::livewire('/dashboard', 'pages::dashboard');
Route::livewire('/articles/{article}', 'pages::article.edit');

// L'ancienne syntaxe reste supportée
Route::get('/profil', App\Livewire\Profil::class);

Les namespaces pages:: et layouts:: sont configurés par défaut. Ajoutez vos propres namespaces dans config/livewire.php :

// config/livewire.php
'component_namespaces' => [
    'layouts' => resource_path('views/layouts'),
    'pages' => resource_path('views/pages'),
    'admin' => resource_path('views/admin'), // Namespace personnalisé
],

Migration depuis Livewire 3

La migration reste simple. Les composants class-based fonctionnent toujours. Points d’attention :

  • Configuration : layout devient component_layout, lazy_placeholder devient component_placeholder
  • wire:model : n’écoute plus les événements des enfants par défaut (ajoutez .deep si nécessaire)
  • wire:transition : utilise désormais l’API View Transitions native (les modifiers .opacity, .scale sont supprimés)
  • Volt : si vous utilisez Volt, remplacez simplement use Livewire\Volt\Component par use Livewire\Component
# Convertir un composant class-based en single-file
php artisan livewire:convert nom-du-composant

Bonnes pratiques Livewire 4

  • Privilégiez les single-file components pour les composants simples à moyens
  • Utilisez les islands pour isoler les parties coûteuses en requêtes
  • Combinez #[Computed] avec les islands pour optimiser les requêtes SQL
  • Exploitez wire:show et wire:text avant de recourir à des requêtes serveur
  • Ajoutez wire:key dans toutes vos boucles @foreach

Ressources

À propos de Laravel Actu

Développeur passionné par Laravel et son écosystème.

Voir tous les articles
Partager :

Ne manquez aucune actualité Laravel

Recevez les meilleurs articles et tutoriels directement dans votre boîte mail.

Pas de spam. Désinscription possible à tout moment.