Symfony AssetMapper: Bundler war gestern
AI generated
SF
{ }
Symfony · AssetMapper · Import Maps · ES-Module
Symfony AssetMapper:
Bundler war gestern

Webpack konfigurieren, npm-Pakete warten, Build-Pipelines debuggen — das kostet in Symfony-Projekten oft mehr Zeit als die eigentliche Frontendlogik. Symfony AssetMapper macht den Bundler für die meisten Projekte überflüssig: Import Maps, automatisches Versioning und ES-Module direkt aus PHP, ohne Node.js-Build-Schritt.

16 Min. Lesezeit AssetMapper · Import Maps · Stimulus · Versioning · Migration Symfony 6.3+ · PHP 8.2+ · Keine Node.js-Pflicht

1. Warum Symfony AssetMapper den Bundler ersetzt

Der klassische Frontend-Workflow in Symfony-Projekten sah jahrelang so aus: Webpack Encore konfigurieren, npm install ausführen, Build-Fehler nach Node.js-Updates debuggen, separate Watcher-Prozesse neben dem PHP-Entwicklungsserver betreiben. Das alles, bevor auch nur ein JavaScript geschrieben wurde. Symfony AssetMapper, eingeführt in Symfony 6.3 und stabilisiert in 6.4, beseitigt diese gesamte Schicht: Es nutzt native Browser-Features — konkret Import Maps und ES-Module — die heute in allen modernen Browsern unterstützt werden, ohne dass ein Bundling-Schritt nötig wäre.

Der entscheidende technologische Wandel dahinter: Aktuelle Browser können ES-Module nativ auflösen. Das bedeutet, ein import { createApp } from 'vue' im Browser funktioniert ohne Bundler, sofern eine Import Map dem Browser sagt, unter welcher URL das Paket liegt. Symfony AssetMapper verwaltet genau diese Import Maps — es mappt Paketnamen auf versionierte URLs, sorgt für Cache-Busting durch Datei-Hashes und liefert Assets direkt aus dem assets/-Verzeichnis aus, ohne Kompilierungsschritt. Für Projekte ohne komplexe Build-Pipelines — und das sind in der Praxis die meisten traditionellen Symfony-Webanwendungen — ist das ein erheblicher Gewinn an Einfachheit.

2. Installation und Erstkonfiguration

Symfony AssetMapper ist seit Symfony 6.3 Bestandteil des Symfony-Kernpakets und benötigt keine externe Installation. In neuen Projekten, die mit symfony new --webapp erstellt werden, ist es standardmäßig aktiv. In bestehenden Projekten installiert man es über composer require symfony/asset-mapper symfony/asset. Der Symfony Flex Recipe legt automatisch die Verzeichnisstruktur an: assets/app.js als Einstiegspunkt, assets/styles/app.css für globale Stile und die importmap.php-Datei im Projektstamm, welche die Import-Map-Konfiguration enthält.

Die Konfiguration in config/packages/asset_mapper.yaml legt fest, welche Verzeichnisse Symfony AssetMapper als Asset-Quellen verwaltet. Standardmäßig ist das nur assets/, aber beliebig viele Pfade lassen sich hinzufügen — etwa wenn ein Bundle eigene Assets mitbringt oder Assets aus mehreren Modulen zusammengeführt werden sollen. In Templates bindet man Assets mit der Twig-Funktion asset() ein, die automatisch den versionierten Pfad mit Hash-Suffix erzeugt. Das Import-Map-Tag { { importmap('app') } } injiziert die vollständige Import-Map sowie den Einstiegspunkt in das HTML-Dokument — eine einzige Zeile ersetzt den kompletten script- und link-Block, den Webpack Encore früher generiert hat.


<?php
// importmap.php — managed by Symfony AssetMapper
// Do not edit manually; use: bin/console importmap:require <package>

return [
    'app' => [
        // Entry point: assets/app.js
        'path' => 'app.js',
        'entrypoint' => true,
    ],
    '@hotwired/stimulus' => [
        // Downloaded and versioned locally by AssetMapper
        'version' => '3.2.2',
    ],
    '@symfony/stimulus-bundle' => [
        'path' => '@symfony/stimulus-bundle/loader.js',
    ],
    'bootstrap' => [
        'version' => '5.3.3',
    ],
    'bootstrap/dist/css/bootstrap.min.css' => [
        'version' => '5.3.3',
        'type' => 'css',
    ],
];

3. Import Maps: Browser-natives Module-System

Import Maps sind ein Web-Standard (WHATWG), der im Browser nativ verfügbar ist. Sie erlauben es, bare Imports wie import { Controller } from '@hotwired/stimulus' auf konkrete URLs aufzulösen, ohne dass ein Bundler die Imports umschreibt. Das <script type="importmap">-Tag im HTML enthält ein JSON-Objekt, das Paketnamen auf URLs mapped. Symfony AssetMapper generiert dieses Tag automatisch aus der importmap.php-Konfiguration und löst dabei versionierte Asset-URLs auf — inklusive Content-Hash für zuverlässiges Cache-Busting.

Das Resultat im generierten HTML: Der Browser erhält eine Import Map mit allen bekannten Paketen und ihren URLs, gefolgt vom Einstiegspunkt-Skript. Alle Importe in assets/app.js und transitiven Modulen werden vom Browser direkt gegen die Import Map aufgelöst — kein Bundle, kein Webpack-Output, keine CommonJS-Wrapper. Symfony AssetMapper lädt Vendor-Pakete standardmäßig lokal herunter und liefert sie von der eigenen Domain aus, was CSP-Konfiguration erheblich vereinfacht und externe Abhängigkeiten zur Build-Zeit fixiert, statt sie zur Laufzeit von einem CDN zu laden.


{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    {# AssetMapper injects the full Import Map + entrypoint script #}
    { { importmap('app') } }

    {# CSS assets — versioned automatically with content hash #}
    <link rel="stylesheet" href="{ { asset('styles/app.css') } }">
</head>
<body>
    {% block body %}{% endblock %}
</body>
</html>

{# Generated HTML output (simplified): #}
{#
<script type="importmap">
{
  "imports": {
    "app": "/assets/app-a1b2c3d4.js",
    "@hotwired/stimulus": "/assets/vendor/@hotwired/stimulus-e5f6a7b8.js",
    "bootstrap": "/assets/vendor/bootstrap-c9d0e1f2.js"
  }
}
</script>
<script type="module">import 'app';</script>
#}

4. Vendor-Pakete ohne npm einbinden

Das Hinzufügen externer JavaScript-Pakete erfolgt mit Symfony AssetMapper über den Befehl bin/console importmap:require paketname. Der Befehl fügt das Paket zur importmap.php hinzu und lädt es von jsDelivr herunter in das assets/vendor/-Verzeichnis. Von dort wird es als normales Asset versioniert und ausgeliefert — ohne npm, ohne node_modules, ohne Package-Lock-Konflikte. Das Paket ist sofort im JavaScript über seinen Paketnamen importierbar, weil Symfony AssetMapper die Import Map aktualisiert.

Updates von Vendor-Paketen erfolgen mit bin/console importmap:update — es prüft alle eingetragenen Pakete auf neuere Versionen und aktualisiert importmap.php sowie die heruntergeladenen Dateien im assets/vendor/-Verzeichnis. Das assets/vendor/-Verzeichnis gehört nicht in die Versionsverwaltung, sondern wird in der .gitignore ausgeschlossen. Stattdessen committet man nur die importmap.php, und nach einem git clone stellt bin/console importmap:install alle Vendor-Dateien wieder her — analog zu composer install für PHP-Pakete.

5. Automatisches Versioning und Cache-Busting

Eines der zentralen Features von Symfony AssetMapper ist das automatische Versioning aller Assets. Jede Datei im überwachten assets/-Verzeichnis erhält beim Deployment einen Content-Hash als Teil des Dateinamens: app.js wird zu app-a1b2c3d4.js. Das ermöglicht aggressive Browser-Caching mit langen Cache-Control: max-age-Headern, weil sich der Dateiname bei jeder Änderung ändert und somit immer frische Inhalte laden. Twig-Templates nutzen die asset()-Funktion, die automatisch den versionierten Pfad auflöst — im Template steht asset('images/logo.svg'), im HTML-Output erscheint /assets/images/logo-f7e8d9c0.svg.

Im Entwicklungsmodus arbeitet Symfony AssetMapper ohne Hashes und liefert Assets direkt aus dem Dateisystem — jede Änderung ist sofort im Browser sichtbar, ohne Build-Schritt. Im Produktionsmodus erzeugt bin/console asset-map:compile alle versionierten Dateien im public/assets/-Verzeichnis. Dieser Schritt ersetzt den Webpack-Build im Deployment-Workflow und ist erheblich schneller: Keine Transpilierung, kein Tree-Shaking, kein Bundling — nur Datei-Kopieren mit Hash-Umbenennung. Das Ergebnis liegt sofort bereit und das public/assets/-Verzeichnis kann direkt von einem CDN oder Reverse Proxy gecached werden.

6. Stimulus und Symfony UX ohne Build-Schritt

Symfony AssetMapper ist vollständig in das Symfony UX-Ökosystem integriert. Stimulus-Controller, das JavaScript-Mikroframework hinter Symfony UX, laufen ohne Webpack: Nach composer require symfony/ux-stimulus-bundle und bin/console importmap:require @hotwired/stimulus ist Stimulus ohne npm-Schritt verfügbar. Symfony AssetMapper scannt das assets/controllers/-Verzeichnis automatisch nach Stimulus-Controllern und registriert sie, sodass data-controller="hello" im HTML sofort mit assets/controllers/hello_controller.js verbunden ist.

Symfony UX-Komponenten wie Turbo, Chart.js-Integration oder Cropperjs folgen demselben Muster: Das PHP-Paket wird über Composer installiert, Symfony Flex registriert die notwendigen JavaScript-Abhängigkeiten in der importmap.php, und nach bin/console importmap:install sind sie verwendbar. Das assets/controllers.json-Format aus der alten Webpack-Encore-Ära wird von Symfony AssetMapper ebenfalls unterstützt, was die Migration von bestehenden UX-Projekten vereinfacht. Der gesamte UX-Workflow — Stimulus-Controller schreiben, in Templates verwenden, im Browser sehen — funktioniert ohne einen einzigen npm-Befehl.


// assets/controllers/dropdown_controller.js
// Stimulus controller — no build step needed with Symfony AssetMapper
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    // Define target elements accessible via this.xTarget
    static targets = ['menu', 'button'];

    // Define values with type and default
    static values = {
        open: { type: Boolean, default: false },
    };

    connect() {
        // Called when the controller connects to the DOM
        this.updateMenu();
    }

    toggle() {
        this.openValue = !this.openValue;
    }

    openValueChanged() {
        this.updateMenu();
    }

    updateMenu() {
        // Toggle ARIA and visibility based on state value
        this.menuTarget.hidden = !this.openValue;
        this.buttonTarget.setAttribute('aria-expanded', String(this.openValue));
    }
}

7. CSS mit AssetMapper verwalten

CSS-Dateien verwaltet Symfony AssetMapper wie JavaScript-Assets: Jede Datei im assets/-Verzeichnis wird versioniert und per asset()-Funktion referenziert. Globale Stile liegen in assets/styles/app.css, komponentenspezifische CSS-Dateien in Unterordnern. CSS-Importe per @import werden von Symfony AssetMapper aufgelöst — allerdings ohne PostCSS-Transformationen, weil kein Build-Schritt läuft. Das bedeutet: Vanilla-CSS-Features wie Custom Properties, :is(), :has() und CSS-Nesting funktionieren dank moderner Browser direkt, während CSS-Präprozessoren wie Sass nicht ohne weiteres nutzbar sind.

Wer Tailwind CSS in einem Symfony AssetMapper-Projekt nutzen möchte, greift auf das Tailwind CLI-Binary zurück, das unabhängig von npm funktioniert: ./tailwindcss -i assets/styles/app.css -o public/assets/styles/app.min.css --watch überwacht Änderungen und baut die CSS-Datei ohne Node.js-Projekt neu. Im Deployment-Workflow läuft das Tailwind CLI als separater Build-Schritt, bevor asset-map:compile die restlichen Assets kompiliert. Diese Kombination ermöglicht Tailwind ohne das volle npm-Ökosystem: kein package.json, kein node_modules, nur das Standalone-Binary.

8. Migration von Webpack Encore zu AssetMapper

Die Migration eines bestehenden Symfony-Projekts von Webpack Encore zu Symfony AssetMapper ist in den meisten Fällen ein Wochenendsworkflow, kein Monats-Projekt. Der erste Schritt: composer require symfony/asset-mapper und bin/console importmap:require für alle JavaScript-Pakete, die bisher in package.json standen. Der zweite Schritt: JavaScript-Dateien von CommonJS-Syntax (require(), module.exports) auf ES-Module-Syntax (import, export) umstellen, da Symfony AssetMapper ausschließlich mit ES-Modulen arbeitet. Das ist in modernen Projekten meist trivial — in älteren Projekten mit vielen CommonJS-Abhängigkeiten kann dieser Schritt aufwändiger sein.

Kritischer Punkt bei der Migration: Pakete, die kein ES-Modul-Format anbieten und nur als CommonJS verfügbar sind, funktionieren nicht direkt mit Symfony AssetMapper. jsDelivr liefert für die meisten populären Pakete ESM-Varianten aus, aber es gibt Ausnahmen. Legacy-Pakete, die intern auf window-Globals angewiesen sind oder CommonJS-spezifische Features nutzen, müssen entweder durch ES-Modul-kompatible Alternativen ersetzt oder weiterhin über Webpack gebündelt werden. Für solche Ausnahmen ist ein Hybrid-Ansatz möglich: Webpack bündelt nur die problematischen Teile als IIFE, Symfony AssetMapper verwaltet alles andere.

Aufgabe Webpack Encore Symfony AssetMapper Unterschied
Paket hinzufügen npm install paket importmap:require paket Kein node_modules, kein package.json
Build-Schritt npm run build asset-map:compile Sekunden statt Minuten
Entwicklungsmodus Watcher-Prozess nötig Kein Watcher nötig Änderungen sofort sichtbar
Versioning Webpack contenthash Automatisch per Hash Gleichwertig, ohne Konfiguration
Legacy CommonJS Vollständig unterstützt Nur ESM Ältere Pakete ggf. problematisch

9. AssetMapper vs. Webpack Encore im Vergleich

Symfony AssetMapper gewinnt bei Einfachheit, Wartbarkeit und Deployment-Geschwindigkeit gegenüber Webpack Encore. Der Wegfall von Node.js-Abhängigkeiten bedeutet weniger Versionskonflikte, weniger Breaking Changes durch npm-Updates und eine sauberere Projektstruktur. In Docker-basierten Deployments entfällt der separate Build-Container für Frontend-Assets — bin/console asset-map:compile läuft im PHP-Container und ist in Sekunden abgeschlossen. Das ist ein echter Gewinn für kleinere Teams, die keine dedizierte Frontend-Build-Infrastruktur betreiben wollen.

Webpack Encore bleibt überlegen, wenn das Projekt TypeScript mit komplexen Konfigurationen, CSS-Präprozessoren wie Sass oder Less, aggressive Code-Splitting-Strategien oder Abhängigkeiten auf CommonJS-only-Pakete benötigt. Für Projekte mit dedizierten Frontend-Entwicklern, die eine vollständige Build-Pipeline mit Linting, Testing und Transpilierung erwarten, liefert Webpack oder Vite mehr Kontrolle. Symfony AssetMapper richtet sich explizit an die Mehrheit der Symfony-Projekte, die modernes JavaScript benötigen, aber keine komplexe Build-Pipeline rechtfertigen können.

Mironsoft

Symfony-Entwicklung, Frontend-Modernisierung und AssetMapper-Migration

Webpack-Komplexität hinter euch lassen?

Wir migrieren Symfony-Projekte von Webpack Encore zu AssetMapper — Analyse der bestehenden Assets, ESM-Umstellung, Stimulus-Integration und saubere Deployment-Pipeline ohne Node.js-Overhead.

Migration

Analyse bestehender Webpack-Konfiguration, ESM-Umstellung und Import-Map-Aufbau

Stimulus-Integration

Symfony UX-Komponenten und Stimulus-Controller ohne Build-Schritt einrichten

Deployment

asset-map:compile in CI/CD-Pipeline integrieren und CDN-Caching einrichten

10. Zusammenfassung

Symfony AssetMapper macht den Node.js-Bundler für die meisten Symfony-Projekte überflüssig. Import Maps ermöglichen native ES-Module-Auflösung im Browser ohne Webpack-Konfiguration. Vendor-Pakete werden per importmap:require heruntergeladen und lokal ausgeliefert — ohne node_modules, ohne package.json. Automatisches Versioning durch Content-Hashes ermöglicht aggressives Browser-Caching ohne Konfigurationsaufwand. Stimulus und das gesamte Symfony UX-Ökosystem funktionieren ohne Build-Schritt. Deployments werden schneller, einfacher und unabhängiger von Node.js-Versionen.

Der Wechsel lohnt sich für alle Symfony-Projekte, die kein komplexes Build-Setup benötigen: traditionelle Webanwendungen, E-Commerce-Systeme, CMS-Projekte und APIs mit Administrationsoberflächen. Webpack Encore bleibt die richtige Wahl, wenn TypeScript, Sass oder Legacy-CommonJS-Pakete unverzichtbar sind. Für alle anderen Projekte ist Symfony AssetMapper der direkteste Weg zu modernem JavaScript ohne Bundler-Komplexität.

Symfony AssetMapper — Das Wichtigste auf einen Blick

Kein Bundler nötig

Import Maps + ES-Module im Browser ersetzen Webpack für die meisten Symfony-Projekte. Kein npm, kein node_modules, kein Build-Watcher.

Vendor-Pakete per CLI

bin/console importmap:require paket lädt Pakete von jsDelivr und trägt sie in die Import Map ein. importmap:install stellt nach git clone wieder her.

Automatisches Versioning

Content-Hashes für alle Assets ohne Konfiguration. asset() in Twig löst automatisch den versionierten Pfad auf.

Stimulus & UX

Symfony UX-Komponenten und Stimulus-Controller funktionieren vollständig ohne Build-Schritt. Controller-Auto-Discovery inklusive.

11. FAQ: Symfony AssetMapper

1Was ist Symfony AssetMapper?
Symfony-Komponente ab 6.3 für Frontend-Assets ohne Node.js-Bundler. Nutzt Import Maps und native ES-Module, versioniert per Content-Hash und macht Webpack für viele Projekte überflüssig.
2Node.js erforderlich?
Nein. Pakete werden per importmap:require heruntergeladen, asset-map:compile läuft in PHP. Kein npm, kein node_modules.
3Stimulus mit AssetMapper?
Ja. Nach composer require symfony/ux-stimulus-bundle und importmap:require @hotwired/stimulus ist Stimulus ohne Build-Schritt aktiv. auto-discover aus assets/controllers/ inklusive.
4importmap:require vs. npm install?
importmap:require lädt von jsDelivr in assets/vendor/ und aktualisiert importmap.php. Kein node_modules, kein package.json, direkt als ES-Modul importierbar.
5TypeScript mit AssetMapper?
Nicht direkt — TypeScript erfordert Transpilierung. Für TypeScript ist Webpack Encore oder Vite die bessere Wahl.
6Wie funktioniert Versioning?
Content-Hash im Dateinamen: app.js → app-a1b2c3.js. asset() in Twig löst automatisch den versionierten Pfad auf. Aggressives Browser-Caching ohne Cache-Invalidierungsprobleme.
7Tailwind mit AssetMapper?
Über das Tailwind Standalone CLI-Binary ohne Node.js. Läuft als separater Build-Schritt vor asset-map:compile im Deployment.
8Migration von Webpack Encore?
1. composer require symfony/asset-mapper, 2. CommonJS auf ESM umstellen, 3. Pakete per importmap:require hinzufügen. Meist 1–3 Tage Aufwand je nach Projektgröße.
9Vendor-Dateien bei Deployment?
assets/vendor/ liegt in .gitignore. importmap:install stellt alle Vendor-Dateien nach git clone wieder her — analog zu composer install.
10Browser-Unterstützung für Import Maps?
Alle modernen Browser seit Chrome 89, Firefox 108, Safari 16.4. Für ältere Browser injiziert AssetMapper optional das es-module-shims-Polyfill.