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 laLivePropqueryà 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-component2. 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"≈ unvalue/onChangecontrôlé, mais l’état vit sur le serveur et le re-render est serveur. Là où React garderaitqueryen 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" ?
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] ?
#[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.