Tous les articles
Sam. 25 avril 2026 · 5 min de lecture

✨ Symfony UX : interfaces réactives en Twig, sans framework JS

Symfony UX

📚 Introduction

Symfony UX est probablement le projet Symfony le plus sous-évalué côté grand public. C’est un ensemble de bundles et de packages JavaScript qui transforment Twig en couche de rendu réactive, sans imposer React ou Vue. L’angle est radicalement différent de ce que propose Inertia côté Laravel ou Livewire : tu restes en PHP, tu décris ton interface en Twig, et Symfony UX se charge de la rendre interactive via Stimulus, Turbo et un protocole Ajax interne.

Je tombe régulièrement sur des projets Symfony où l’équipe a monté un SPA Vue ou React pour un back-office… qui aurait pu être construit en deux fois moins de temps avec Symfony UX. Ce post est un tour d’horizon pratique : quand UX est le bon choix, quels packages composent l’écosystème, et comment ça s’imbrique en 2026.

🧱 La pile : Stimulus + Turbo + Components

Symfony UX repose sur trois piliers empruntés au monde Hotwire (Basecamp) et étendus par l’équipe Symfony.

  • Stimulus : un mini framework JS qui attache un comportement à un élément DOM via des data-controller. Très léger, parfait pour les widgets isolés.
  • Turbo : la navigation devient SPA-like sans framework. Les pages s’échangent par fragments, les formulaires postent en Ajax, les frames se rechargent indépendamment.
  • Twig Components et Live Components : la composition côté Twig, avec une variante réactive qui re-render le composant via Ajax quand son state change.

Tu ajoutes ce qu’il te faut au-dessus, package par package. Si tu connais Astro et son approche island, tu retrouves un esprit proche : du HTML rendu côté serveur, des îlots interactifs ciblés.

🧩 Twig Components : composer du Twig comme React

Twig Components, c’est le rangement des composants Twig. Tu écris une classe PHP et un template Twig, tu invoques le composant comme une balise.

// src/Twig/Components/Alert.php
namespace App\Twig\Components;
 
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 
#[AsTwigComponent]
final class Alert
{
	public string $type = 'info';
	public string $message;
}
{# templates/components/Alert.html.twig #}
<div class="alert alert-{{ type }}">
	{{ message }}
</div>
{# Utilisation #}
<twig:Alert type="success" message="Profil mis à jour" />

C’est tout. Tu obtiens un système de composants typé, autocomplétable côté IDE, qui se substitue à l’enchevêtrement de {% include %} qu’on traîne depuis 10 ans. Pour les apps internes ou les back-offices, c’est un gain net de lisibilité.

⚡ Live Components : la réactivité sans JS custom

Live Components, c’est le module qui me convertit à chaque démo. Tu prends un Twig Component, tu le marques #[AsLiveComponent], tu déclares quelques LiveProp, et le composant devient réactif. Chaque modification de prop déclenche un re-render via Ajax, sans toucher au JS.

// src/Twig/Components/ProductSearch.php
namespace App\Twig\Components;
 
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
 
#[AsLiveComponent]
final class ProductSearch
{
	use DefaultActionTrait;
 
	#[LiveProp(writable: true)]
	public string $query = '';
 
	public function __construct(
		private ProductRepository $productRepository,
	) {}
 
	public function getProducts(): array
	{
		return $this->productRepository->search($this->query);
	}
}
{# templates/components/ProductSearch.html.twig #}
<div {{ attributes }}>
	<input type="search" data-model="query">
 
	<ul>
		{% for product in this.products %}
			<li>{{ product.name }}</li>
		{% endfor %}
	</ul>
</div>

Tu tapes dans l’input, Symfony envoie une requête Ajax avec le nouveau query, re-rend le composant et patch le DOM. Aucun fichier JS écrit, aucun framework front installé. Le rendu reste autoritaire côté serveur (la source de vérité, c’est PHP).

C’est le pattern que Livewire popularise côté Laravel. Côté Symfony, l’intégration est plus jeune mais déjà très solide.

🛠️ Les packages métiers prêts à l’emploi

Au-delà du socle, Symfony UX livre une vingtaine de packages qui couvrent les besoins récurrents. Les plus utiles à connaître :

  • Autocomplete : transforme un <select> en select Ajax avec recherche, parfait pour de longues listes (utilisateurs, produits).
  • Chartjs : génération de charts depuis PHP, templated en Twig.
  • Map : intégration Leaflet / Google Maps, déclarée côté Symfony.
  • Icons : pipeline d’icônes SVG (Iconify) servies par Twig sans pénaliser le bundle JS.
  • Image Cropper : recadrage d’images directement dans un Form.
  • Dropzone : upload stylisé.
  • Notify : notifications navigateur natives.
  • Toolkit : briques de design system, utile pour démarrer un back-office propre.
  • Translator : pousser les traductions Symfony vers ton JS sans dupliquer.
  • React et Vue.js : si tu veux quand même rendre un composant React ou Vue depuis Twig, ces packages les invoquent comme un Twig Component.

L’installation se fait à la carte. Tu n’embarques pas tout, tu prends ce qui sert.

composer require symfony/ux-autocomplete
composer require symfony/ux-live-component
composer require symfony/ux-twig-component

🎯 Quand choisir Symfony UX

  • Tu construis un back-office ou un dashboard admin : Live Components remplace 80 % d’un SPA Vue/React.
  • Tu travailles en équipe full-stack Symfony : pas besoin d’embaucher un dev front pour ajouter de l’interactivité.
  • Tu veux un storefront e-commerce Twig rapide à itérer (Sylius reste compatible) sans descendre dans une SPA.
  • Tu prototypes vite et tu veux éviter le tooling JS (webpack, Vite, bundlers tiers) : tu restes en AssetMapper Symfony.

⚠️ Quand éviter

  • Tu construis une app mobile native : le rendu serveur n’a aucun intérêt, prends une API + un client natif.
  • Tu vises plusieurs frontends consommateurs (web + mobile + agents IA) : passe headless, Sylius + API Platform ou un équivalent.
  • Ton équipe est majoritairement front et veut un workflow React/Next.js : ne va pas contre.
  • Tu fais du temps réel collaboratif (Figma-like) : Live Components fait du polling et de l’Ajax, pas du collab CRDT.

⚠️ Quelques précautions

  • AssetMapper vs Encore : Symfony UX fonctionne avec les deux, mais AssetMapper (importmaps) est la voie recommandée pour les nouveaux projets. Pas de Webpack, pas de build.
  • Latence Ajax : chaque mutation de LiveProp déclenche une requête. Sur un réseau lent, l’expérience devient saccadée. Pense au modifier debounce() sur data-model (par défaut 150 ms) et à data-model="on(change)|..." pour ne déclencher qu’à la perte du focus.
  • Sécurité : un LiveProp writable: true peut être modifié côté client. Toujours valider côté serveur, ne jamais faire confiance aux props.
  • SEO : Turbo passe en SPA-like, mais la première page reste rendue côté serveur. Pas d’impact négatif si tu utilises bien <meta name="turbo-cache-control" content="no-cache"> sur les pages dynamiques.

🎉 Conclusion

Symfony UX est la réponse Symfony à un constat simple : la majorité des sites n’ont pas besoin de React ou de Vue. Ils ont besoin d’un peu d’interactivité ciblée, sans complexité de toolchain ni de double codebase. Live Components rend ça possible pour un coût d’apprentissage minime quand on connaît déjà Symfony.

Pour 2026, je le recommande systématiquement aux équipes qui démarrent un back-office, une app interne, ou un site B2B. Pour les projets headless ou multi-clients, l’API Platform reste le bon outil. Les deux ne sont pas concurrents, ils répondent à des besoins différents.

🔗 Liens utiles