C'est le syndrome classique. Vous faites un site Symfony propre, rapide, SEO-friendly. Et là, le client demande : "Ce serait bien si la liste se mettait à jour quand je tape dans la recherche, sans recharger la page."
Le développeur en vous soupire. "Bon, il faut que je crée une API JSON, que j'installe React/Vue, que je configure Webpack/Vite, que je gère le state, que je duplique mes templates Twig en JSX..."
Stop. Vous n'a vez pas besoin d'une usine à gaz pour une fonctionnalité d'interaction. Vous avez besoin des Live Components.
La réactivité sans le "JS Fatigue"
Live Components (symfony/ux-live-component), c'est la magie de la réactivité, mais pilotée par le serveur.
Le principe est simple et redoutable :
- Le composant s'affiche en HTML (Server Side Rendering).
- L'utilisateur tape dans un input (
<input data-model="query">). - Au lieu de gérer ça en JS, le composant envoie une requête AJAX légère à Symfony.
- Symfony met à jour la propriété PHP
$query, ré-exécute le rendu Twig. - Le serveur renvoie le nouveau HTML.
- Le composant se met à jour intelligemment (diffing de DOM) sans perdre le focus.
Vous n'avez écrit AUCUNE ligne de JavaScript. Pas d'API. Pas de Serializer. Pas de duplication de logique.
La recherche en temps Réel
Plutôt qu'un exemple théorique, regardez le code réel de mon composant de recherche (src/Twig/Component/Content/Search.php). On est loin du "Hello World".
#[AsLiveComponent(name: 'Content:Search', template: 'components/Content/Search.html.twig')]
class Search
{
use DefaultActionTrait; // Gère les actions automatiques
#[LiveProp(writable: true)]
#[ExposeInTemplate]
public string $query = '';
#[LiveProp(writable: true)]
#[ExposeInTemplate]
public ?string $category = null;
public function __construct(
// On injecte Meilisearch directement, pas besoin de passer par un Controller !
private readonly MeilisearchSearchService $meilisearchSearchService,
) {}
#[ExposeInTemplate('searchResults')]
public function getSearchResults(): array
{
// 1. On nettoie l'entrée utilisateur
$query = $this->normalizedQuery();
if (\strlen($query) < 3) {
return [];
}
// 2. On interroge le moteur de recherche
// Cette méthode est rejouée à chaque frappe côté serveur !
return $this->meilisearchSearchService->search(
$query,
limit: 8,
filters: ['category' => $this->category],
sort: 'createdAt:desc'
);
}
// ... méthode normalizedQuery() pour éviter les injections ...
}
Et le template ? Il est d'une simplicité enfantine.
<div data-controller="search">
<!-- Le champ est lié au PHP via data-model="query" -->
<!-- L'attribut data-loading gère l'UX pendant la requête réseau -->
<div class="relative">
<input type="search" data-model="query" placeholder="Rechercher..." />
<!-- Spinner SVG affiché uniquement pendant le chargement -->
<svg data-loading="removeClass(hidden)" class="hidden animate-spin">...</svg>
</div>
<!-- Les résultats s'affichent ici -->
<div class="results">
{% for post in this.searchResults %}
<twig:Content:Post :post="post" />
{% else %}
{% if this.hasNoResults %}
<p>Aucun résultat trouvé.</p>
{% endif %}
{% endfor %}
</div>
</div>
C'est tout. Quand l'utilisateur tape, $query est mis à jour côté serveur via une requête AJAX ultra-légère. getSearchResults() est rappelé, et Live Components renvoie le nouveau HTML de la div résultats.
C'est aussi fluide qu'une SPA (Single Page App), mais avec la simplicité developer-experience de PHP.
Quand utiliser Live Components ?
Est-ce que ça remplace React pour faire Google Maps ou Trello ? Non. Pour des applis hautement interactives avec gestion d'état complexe côté client, JS reste roi.
Mais pour 90% des besoins web classiques :
- Recherche / Filtres dynamiques
- Pagination sans rechargement
- Formulaires d'édition inline
- Paniers d'achat
- Modales et sidebars
Utiliser React/Vue pour ça en 2026 est de la sur-ingénierie (Over-engineering). Ça alourdit le bundle, complexifie le build, et sépare le front du back inutilement.
Les "super-pouvoirs" cachés
- Validation Temps Réel : Vu que c'est PHP qui tourne, vous utilisez vos
AssertSymfony (#[Assert\Email]). Le formulaire se valide en live, avec les mêmes règles que votre backend. Sécurité maximale. - Synchronisation URL : Ajoutez
url: trueà votre propriété (#[LiveProp(url: true)]). L'URL du navigateur se met à jour quand on tape (?query=foo). Le bouton "Précédent" du navigateur marche. Sans effort.
Le mot de la fin
Live Components redonne le pouvoir au développeur Backend. Il permet de créer des expériences utilisateur "Premium" (réactives, fluides) sans payer la "Taxe JavaScript" (complexité, maintenance).
C'est le chaînon manquant qui permet à Symfony de rivaliser avec les frameworks JS modernes sur leur propre terrain : l'interactivité. Essayez-le sur votre prochaine feature "dynamique". Vous ne reviendrez pas en arrière.