Symfony11 min

Webpack Encore : front-end moderne avec Symfony

Par Pierre-Arthur Demengel
SymfonyWebpack EncoreFrontendCSSJavaScript

Webpack Encore est la solution officielle de Symfony pour gerer les assets front-end - JavaScript, CSS, images et polices. C'est un wrapper autour de Webpack qui simplifie drastiquement la configuration tout en conservant toute la puissance de l'ecosysteme Webpack. Ce guide couvre l'installation, la configuration et les techniques avancees pour un front-end moderne avec Symfony 7.2.

Installation et setup initial

L'installation se fait en deux etapes : le bundle PHP et les dependances npm :

# Installation du bundle
composer require symfony/webpack-encore-bundle

# Installation des dependances npm
npm install

# Ou avec yarn
yarn install

Flex genere automatiquement la structure de fichiers :

assets/
    app.js           # Point d'entree principal
    bootstrap.js     # Initialisation Stimulus
    styles/
        app.css      # Styles principaux
    controllers/     # Controllers Stimulus
webpack.config.js    # Configuration Encore

Configuration de webpack.config.js

Le fichier webpack.config.js est le coeur de la configuration. Encore expose une API fluide qui simplifie les options Webpack :

const Encore = require('@symfony/webpack-encore');

Encore
    // Dossier de sortie (dans public/)
    .setOutputPath('public/build/')
    .setPublicPath('/build')

    // Points d'entree
    .addEntry('app', './assets/app.js')
    .addEntry('admin', './assets/admin.js')

    // Styles sans JavaScript associe
    .addStyleEntry('print', './assets/styles/print.css')

    // Runtime chunk unique (recommande)
    .enableSingleRuntimeChunk()

    // Nettoyage du dossier build a chaque compilation
    .cleanupOutputBeforeBuild()

    // Source maps en developpement
    .enableSourceMaps(!Encore.isProduction())

    // Versioning en production (hash dans les noms de fichiers)
    .enableVersioning(Encore.isProduction())

    // Configuration Babel pour la compatibilite navigateurs
    .configureBabelPresetEnv((config) => {
        config.useBuiltIns = 'usage';
        config.corejs = '3.38';
    })
;

module.exports = Encore.getWebpackConfig();

Entries : organiser vos points d'entree

Chaque addEntry() cree un point d'entree Webpack. Un entry produit un ou plusieurs fichiers JS et CSS :

// assets/app.js - Entry principal
import './styles/app.css';
import './bootstrap.js';

// Votre code applicatif
console.log('Application chargee');

// assets/admin.js - Entry admin (charge uniquement sur /admin)
import './styles/admin.css';
import './admin/dashboard.js';
import './admin/users.js';

Dans vos templates Twig, incluez les entries avec les fonctions Encore :

{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
    {% block stylesheets %}
        {{ encore_entry_link_tags('app') }}
    {% endblock %}
</head>
<body>
    {% block body %}{% endblock %}

    {% block javascripts %}
        {{ encore_entry_script_tags('app') }}
    {% endblock %}
</body>
</html>

{# templates/admin/base.html.twig #}
{% extends 'base.html.twig' %}

{% block stylesheets %}
    {{ parent() }}
    {{ encore_entry_link_tags('admin') }}
{% endblock %}

{% block javascripts %}
    {{ parent() }}
    {{ encore_entry_script_tags('admin') }}
{% endblock %}

Les fonctions encore_entry_link_tags et encore_entry_script_tags generent automatiquement les bonnes balises HTML avec le hash de version en production.

CSS, Sass et PostCSS

Encore supporte plusieurs preprocesseurs CSS. Activez-les dans la configuration :

Encore
    // Sass/SCSS
    .enableSassLoader()

    // PostCSS (avec autoprefixer, tailwindcss, etc.)
    .enablePostCssLoader()

    // Les deux ensemble
    .enableSassLoader()
    .enablePostCssLoader()
;

Pour Sass, installez les dependances :

npm install sass sass-loader --save-dev

Pour PostCSS avec Tailwind CSS :

npm install postcss postcss-loader tailwindcss autoprefixer --save-dev
// postcss.config.js
module.exports = {
    plugins: {
        tailwindcss: {},
        autoprefixer: {},
    },
};

Exemple de structure Sass

// assets/styles/app.scss
@use 'variables';
@use 'base';
@use 'components/header';
@use 'components/footer';
@use 'components/card';
@use 'pages/home';
@use 'pages/product';

// assets/styles/_variables.scss
$primary: #2563eb;
$secondary: #7c3aed;
$font-family: 'Inter', system-ui, sans-serif;
$container-max-width: 1200px;

Asset versioning et cache busting

Le versioning ajoute un hash au nom des fichiers generes. Cela permet un cache agressif (1 an) car chaque modification produit un nouveau nom de fichier :

# Sans versioning
/build/app.js
/build/app.css

# Avec versioning (production)
/build/app.a1b2c3d4.js
/build/app.e5f6g7h8.css

Encore genere un fichier entrypoints.json qui mappe les noms logiques vers les fichiers physiques. Les fonctions Twig lisent ce fichier pour generer les bonnes URLs. Vous n'avez rien a gerer manuellement.

Dev server avec Hot Module Replacement

Le dev server offre une experience de developpement optimale avec le rechargement automatique :

# Compilation classique (watch mode)
npm run watch

# Dev server avec rechargement auto
npm run dev-server

# Dev server avec HMR (remplacement sans rechargement)
npm run dev-server -- --hot

Configuration avancee du dev server :

Encore
    .configureDevServerOptions(options => {
        options.server = {
            type: 'https',
            options: {
                pfx: path.join(process.env.HOME, '.symfony5/certs/default.p12'),
            }
        };
        options.allowedHosts = 'all';
    })
;

Le HMR est particulierement utile pour le CSS : vos modifications de style sont appliquees instantanement sans perdre l'etat de la page.

Code splitting : optimiser le chargement

Le code splitting extrait les modules partages entre plusieurs entries pour eviter la duplication :

Encore
    // Extraire les chunks communs
    .splitEntryChunks()

    // Configuration fine du splitting
    .configureSplitChunks(splitChunks => {
        splitChunks.minSize = 30000;
        splitChunks.cacheGroups = {
            vendors: {
                test: /[\/]node_modules[\/]/,
                name: 'vendors',
                chunks: 'all',
                priority: 10,
            },
        };
    })
;

Avec splitEntryChunks(), si app.js et admin.js utilisent tous les deux lodash, celui-ci est extrait dans un chunk separe charge une seule fois.

Import dynamique et lazy loading

Pour les fonctionnalites chargees conditionnellement, utilisez les imports dynamiques :

// assets/app.js

// Ce module n'est charge que si l'element existe sur la page
const chartElement = document.getElementById('sales-chart');
if (chartElement) {
    import('./components/SalesChart.js').then(({ default: SalesChart }) => {
        new SalesChart(chartElement);
    });
}

// Avec async/await
async function initEditor() {
    const { Editor } = await import('./components/Editor.js');
    return new Editor('#editor');
}

Webpack genere automatiquement des chunks separes pour chaque import dynamique. Le navigateur ne les telecharge que quand ils sont necessaires.

Gestion des images et polices

Encore gere les assets statiques (images, polices) et les copie dans le dossier build avec versioning :

// En CSS/Sass - les URLs sont resolues automatiquement
.hero {
    background-image: url('../images/hero-bg.jpg');
}

@font-face {
    font-family: 'CustomFont';
    src: url('../fonts/CustomFont.woff2') format('woff2');
}
// En JavaScript
import heroImage from '../images/hero.jpg';

const img = document.createElement('img');
img.src = heroImage; // URL versionnee automatiquement

Pour copier des fichiers sans les traiter (favicons, fichiers statiques) :

Encore
    .copyFiles({
        from: './assets/images',
        to: 'images/[path][name].[hash:8].[ext]',
        pattern: /.(png|jpg|jpeg|gif|svg|webp)$/
    })
;

Integration Stimulus Bridge

Le @symfony/stimulus-bridge connecte automatiquement les controllers Stimulus des bundles UX :

Encore
    .enableStimulusBridge('./assets/controllers.json')
;
// assets/controllers.json (genere automatiquement par Flex)
{
    "controllers": {
        "@symfony/ux-chartjs": {
            "chart": {
                "enabled": true,
                "fetch": "eager"
            }
        },
        "@symfony/ux-live-component": {
            "live": {
                "enabled": true,
                "fetch": "eager"
            }
        }
    },
    "entrypoints": []
}

Le bridge detecte les controllers exposes par les packages dans node_modules et les enregistre dans l'application Stimulus. Pas besoin d'import manuel. Pour en savoir plus sur Stimulus et les composants UX, consultez notre article sur Symfony UX en pratique.

Build de production

La commande de build production active automatiquement les optimisations :

# Build de production
npm run build

# Ce qui est active automatiquement :
# - Minification JS (Terser) et CSS
# - Tree-shaking (suppression du code non utilise)
# - Versioning (hash dans les noms de fichiers)
# - Source maps desactivees (sauf si forcees)
# - Optimisation des images

Pour analyser la composition de votre bundle :

# Generer un rapport d'analyse
npm run build -- --analyze

Ce rapport visuel montre la taille de chaque module et aide a identifier les dependances a optimiser. Pour l'integration dans un theme Bootstrap, consultez notre guide sur l'integration Bootstrap avec Symfony. Et si vous envisagez d'integrer React dans votre front-end Symfony, lisez notre article sur l'architecture Symfony + React.

Besoin d'une configuration front-end optimisee pour votre projet Symfony ? Consultez mes tarifs, decouvrez mes services, ou contactez-moi pour en discuter.

Questions fréquentes

13 projets livrésGrand-Est & BelgiqueLighthouse >90Disponible immédiatement

Un projet en tête ?

Discutons de votre site web. Réponse garantie sous 24h.

Ou appelez directement :06 95 41 30 25

WhatsApp
Appeler