Skip to Content

Chapitre 8.3 — LiveComponents

Partie 8 : Symfony UX — Chapitre 3/4 Version de référence : symfony/ux-live-component / Symfony 7.4.

⏱️ TL;DR

  • Un LiveComponent est un composant Twig (chapitre 7.2) rendu réactif : quand son état change dans le navigateur, il se re-rend au serveur et le DOM est mis à jour — sans écrire de JavaScript.
  • #[AsLiveComponent] + des #[LiveProp] (propriétés synchronisées) + des #[LiveAction] (méthodes appelables depuis le navigateur).
  • Liaison type v-model : data-model="query" met à jour la LiveProp query à chaque frappe → re-render.
  • Idéal pour : recherche live, formulaires à champs dépendants, listes filtrables, validation en direct — dans un back-office.
  • Compromis : chaque interaction = un aller-retour serveur. Excellent sur bon réseau / outils internes ; inadapté à l’offline ou aux interactions ultra-fréquentes.
  • Pour un dev Next.js : c’est de la réactivité pilotée par le serveur — l’état vit sur le serveur, le re-render est serveur, le DOM est « morphé ». L’opposé du modèle React (état client).

🎯 Objectifs

  • Rendre un composant Twig réactif avec #[AsLiveComponent].
  • Synchroniser des propriétés (LiveProp) et déclencher des actions (LiveAction).
  • Construire une recherche live sans JS.
  • Comprendre le compromis « aller-retour serveur ».

1. Du composant Twig au composant Live

Rappel (7.2) : un composant Twig = une classe + un template. Un LiveComponent ajoute la réactivité : ses propriétés marquées #[LiveProp] sont synchronisées avec le navigateur, et toute modification déclenche un re-render côté serveur.

composer require symfony/ux-live-component

2. Exemple : une recherche live

// src/Twig/Components/BookingSearch.php use App\Repository\BookingRepository; use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; use Symfony\UX\LiveComponent\Attribute\LiveProp; use Symfony\UX\LiveComponent\DefaultActionTrait; #[AsLiveComponent] final class BookingSearch { use DefaultActionTrait; #[LiveProp(writable: true)] // synchronisée + modifiable depuis le navigateur public string $query = ''; public function __construct(private readonly BookingRepository $bookings) {} // appelée à chaque re-render : les résultats reflètent $this->query courant public function getResults(): array { return $this->bookings->searchByEmail($this->query); } }
{# templates/components/BookingSearch.html.twig #} <div{{ attributes }}> <input type="search" data-model="query" placeholder="Rechercher un client…"> <ul> {% for booking in this.results %} <li>{{ booking.customerEmail }} — {{ booking.status.label }}</li> {% else %} <li>Aucun résultat.</li> {% endfor %} </ul> </div>

Ce qui se passe : data-model="query" lie l’input à la LiveProp query. À chaque frappe (avec un léger debounce), le composant envoie la nouvelle valeur au serveur, qui re-rend le composant avec les résultats filtrés, et le DOM est mis à jour de façon chirurgicale. Zéro ligne de JS écrite.

💡 Pour un dev Next.js : data-model="query" ≈ un value/onChange contrôlé, mais l’état vit sur le serveur et le re-render est serveur. Là où React garderait query en state client et filtrerait côté client (ou via un fetch), LiveComponent fait l’aller-retour et renvoie le HTML mis à jour. Moins de JS, mais un round-trip par changement.

3. LiveAction : des méthodes appelables

Une #[LiveAction] est une méthode du composant qu’on déclenche depuis le navigateur (clic, soumission) ; elle modifie l’état puis re-rend.

use Symfony\UX\LiveComponent\Attribute\LiveAction; #[LiveProp(writable: true)] public int $page = 1; #[LiveAction] public function nextPage(): void { $this->page++; // l'état change → re-render automatique }
<button data-action="live#action" data-live-action-param="nextPage">Page suivante</button>

On peut passer des arguments aux actions, valider un formulaire, ajouter/supprimer des éléments d’une collection (formulaires dynamiques) — le tout piloté par le serveur.

4. Formulaires live et validation en direct

Les LiveComponents brillent avec les formulaires : on peut valider champ par champ à mesure que l’utilisateur tape (via le composant LiveCollectionType, l’intégration Form + Live). Un champ invalide affiche son message immédiatement, sans soumettre toute la page. On construit ce genre de formulaire dynamique en s’appuyant sur la Partie 9 (formulaires).

5. États de chargement et le compromis réseau

Comme chaque interaction déclenche une requête, LiveComponent fournit des indicateurs de chargement (data-loading) pour signaler l’attente. Mais le point clé à assumer :

Chaque interaction = un aller-retour. Sur un bon réseau et pour un outil interne (back-office), c’est fluide et le gain (zéro JS, code serveur unique) est énorme. Pour une UI ultra-interactive, offline-first, ou à très haute fréquence d’événements (drag temps réel, dessin), le modèle SPA (état client) reste supérieur.

⚠️ Piège : vouloir tout faire en LiveComponent « parce que c’est cool ». Sur une interaction par milliseconde ou une app censée fonctionner hors-ligne, l’aller-retour serveur pénalise. LiveComponent est excellent pour le back-office et les formulaires, pas pour remplacer une SPA riche. Le chapitre 8.4 pose la frontière.

✏️ Exercices

Exercice 1. Transformez un champ de recherche statique en recherche live filtrant une liste de prestataires par nom. Quels attributs/annotations utilisez-vous ?

✅ Solution

Un #[AsLiveComponent] avec une #[LiveProp(writable: true)] public string $query = '';, une méthode getResults() qui interroge ProviderRepository::searchByName($this->query). Dans le template : <input data-model="query"> lié à la prop, et une boucle sur this.results. À chaque frappe, le composant se re-rend au serveur avec les résultats filtrés — sans JS.

Exercice 2. Pourquoi ne pas utiliser un LiveComponent pour un canevas de dessin où l’utilisateur trace au pixel près ?

✅ Solution

Parce que chaque mouvement déclencherait un aller-retour serveur : latence inacceptable pour une interaction au pixel/à haute fréquence, et charge serveur énorme. Ce cas exige un état client (JS/canvas, ou une SPA). LiveComponent est fait pour des interactions espacées (recherche, pagination, formulaires), pas pour du temps réel local intensif.

Exercice 3. En quoi l’état d’un LiveComponent diffère-t-il fondamentalement de l’état d’un composant React ?

✅ Solution

Dans un LiveComponent, l’état vit sur le serveur (les LiveProp), et le re-render se fait au serveur (qui renvoie du HTML appliqué au DOM). Dans React, l’état vit côté client et le re-render est local (virtual DOM). Conséquence : LiveComponent = moins de JS mais un round-trip par interaction ; React = interactions instantanées mais tout l’état/logique côté client.

🧠 Quiz de révision

1. Qu’ajoute un LiveComponent à un composant Twig ?

La réactivité : ses #[LiveProp] sont synchronisées avec le navigateur et tout changement provoque un re-render côté serveur (sans JS écrit).

2. Que fait data-model="query" ?

Il lie un champ à la LiveProp query (façon v-model) : chaque changement met à jour la prop côté serveur et re-rend le composant.

3. Qu’est-ce qu’une #[LiveAction] ?

Une méthode du composant déclenchée depuis le navigateur (clic/soumission) qui modifie l’état puis provoque un re-render.

4. Quel est le compromis principal des LiveComponents ?

Chaque interaction = un aller-retour serveur. Excellent sur bon réseau / outils internes ; inadapté à l’offline ou aux interactions très fréquentes.

5. Où vit l’état d’un LiveComponent ?

Sur le serveur (les LiveProp), contrairement à React où l’état vit côté client.


Prochain chapitre : 8.4 — UX vs SPA React — le comparatif décisif : quand Symfony UX, quand Next.js.