Pourquoi quitter Webpack Encore
Webpack Encore a longtemps été le standard Symfony pour le front. Mais sur mes projets React, le constat était le même : démarrage du dev server en 15-20 secondes, hot reload paresseux, configuration verbeuse. Vite résout tout ça — démarrage quasi instantané grâce aux modules ES natifs, HMR en quelques millisecondes, et une configuration minimale. Voici comment je l'intègre proprement dans un projet Symfony.
L'installation : pentatrion/vite-bundle
Le pont entre Symfony et Vite, c'est le bundle pentatrion/vite-bundle, qui fournit les fonctions Twig pour injecter les assets — l'équivalent de ce que faisait Encore.
composer require pentatrion/vite-bundle npm install
La recipe Flex crée un dossier assets/ avec un vite.config.js de base. On ajoute React :
npm install react react-dom npm install -D @vitejs/plugin-react
Le cœur du sujet : les entry points
Un entry point est un fichier JS/CSS que Vite compile comme point d'entrée indépendant. Dans une architecture Symfony + îlots React (celle que j'utilise sur baccalaureat.sn), chaque zone interactive a son propre entry point — on ne charge pas tout React sur une page qui n'en a pas besoin.
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import symfonyPlugin from 'vite-plugin-symfony';
export default defineConfig({
plugins: [
react(),
symfonyPlugin(),
],
build: {
rollupOptions: {
input: {
app: './assets/app.js', // JS global (Turbo, styles)
quiz: './assets/quiz/index.jsx', // ilot React : le quiz
chat: './assets/chat/index.jsx', // ilot React : l'assistant IA
},
},
},
});Trois entry points = trois bundles indépendants. La page d'accueil ne charge que app, la page quiz charge app + quiz. Le gain de poids est immédiat par rapport à un bundle monolithique.
Injecter les assets dans Twig
Le bundle fournit deux fonctions Twig qui remplacent encore_entry_script_tags :
{# templates/base.html.twig #}
{% block stylesheets %}
{{ vite_entry_link_tags('app') }}
{% endblock %}
{% block javascripts %}
{{ vite_entry_script_tags('app') }}
{% endblock %}{# templates/quiz/show.html.twig #}
{% block javascripts %}
{{ parent() }}
{{ vite_entry_script_tags('quiz') }}
{% endblock %}
<div id="quiz-root"
data-matiere="{{ matiere }}"
data-serie="{{ serie }}"></div>La magie du bundle : en dev, ces fonctions pointent vers le serveur Vite (localhost:5173) avec HMR actif ; en prod, elles lisent le manifest du build et injectent les fichiers hashés. Le même code Twig fonctionne dans les deux environnements.
Monter l'îlot React
Chaque entry point React monte son composant sur sa div, en lisant ses props depuis les data-attributes rendus par Twig :
// assets/quiz/index.jsx
import { createRoot } from 'react-dom/client';
import QuizApp from './QuizApp';
const el = document.getElementById('quiz-root');
if (el) {
createRoot(el).render(
<QuizApp
matiere={el.dataset.matiere}
serie={el.dataset.serie}
/>
);
}Le if (el) n'est pas optionnel : l'entry point peut être chargé sur une page où la div n'existe pas (cache Turbo, layout partagé). Sans le garde, c'est le crash JS silencieux garanti.
Le workflow dev
# Terminal 1 : Symfony symfony serve # Terminal 2 : Vite en mode dev (HMR) npm run dev
On navigue sur le site servi par Symfony, et chaque modification d'un composant React est reflétée sans rechargement de page, état conservé. C'est là que Vite écrase Encore : le cycle modification → résultat visible passe de plusieurs secondes à l'instantané.
Le build de production
npm run build # genere public/build/ avec les fichiers hashes + manifest.json
Sur mon VPS, le déploiement enchaîne simplement :
git pull composer install --no-dev --optimize-autoloader npm ci && npm run build php bin/console cache:clear --env=prod
Aucun Node en runtime : Vite ne sert qu'au build, Apache sert des fichiers statiques hashés avec cache long. Le VPS ne fait tourner que PHP-FPM.
Ce que je retiens
- Un entry point par îlot React : on ne charge que le JS nécessaire à chaque page.
- vite_entry_script_tags() gère seul la bascule dev (serveur Vite + HMR) / prod (manifest hashé).
- Les props passent de Twig à React via les data-attributes — simple et sans API supplémentaire.
- Toujours garder le if (el) avant le createRoot : un entry point peut vivre sur une page sans sa div.
- En prod, Node n'existe plus : Vite est un outil de build, pas un serveur.