Repository: symfony/ux
Pull request #2201 (2024-09-24 01:22:08)

[TwigComponent] Optimize ComponentFactory
| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| Issues        | Fix #... 
| License       | MIT

Some internal refactor focused on the Component Factory / Anonymous components usage.

* Optimize the hot path
* Store anonymous template resolution
* Avoid anonymous checks for class-based components
* Add ComponentFactory unit tests
* Add ComponentMetadata::isAnonymous() method
* Fix loop
* Reuse metadata to instanciate and mount component

Other PRs will follow :)

smnandre: (ignore the fabpot one)

Patch:
diff --git a/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php b/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php
index c45b8409..e79e6c5e 100644
--- a/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php
+++ b/src/LiveComponent/src/DependencyInjection/LiveComponentExtension.php
@@ -233,6 +233,10 @@ final class LiveComponentExtension extends Extension implements PrependExtension
             ->addTag('kernel.event_subscriber');
 
         $container->register('ux.live_component.defer_live_component_subscriber', DeferLiveComponentSubscriber::class)
+            ->setArguments([
+                new Reference('ux.twig_component.component_stack'),
+                new Reference('ux.live_component.live_controller_attributes_creator'),
+            ])
             ->addTag('kernel.event_subscriber')
         ;
 
diff --git a/src/Map/CHANGELOG.md b/src/Map/CHANGELOG.md
index 3883fe05..d4894408 100644
--- a/src/Map/CHANGELOG.md
+++ b/src/Map/CHANGELOG.md
@@ -7,9 +7,6 @@
 -   Add `ux_map` Twig function (replaces `render_map` with a more flexible 
     interface)
 -   Add `<twig:ux:map />` Twig component
--   The importmap entry `@symfony/ux-map/abstract-map-controller` can be removed
-    from your importmap, it is no longer needed. 
--   Add `Polygon` support
 
 ## 2.19
 
diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts
index f7f9ffd8..142c2f69 100644
--- a/src/Map/assets/dist/abstract_map_controller.d.ts
+++ b/src/Map/assets/dist/abstract_map_controller.d.ts
@@ -3,12 +3,11 @@ export type Point = {
     lat: number;
     lng: number;
 };
-export type MapView<Options, MarkerOptions, InfoWindowOptions, PolygonOptions> = {
+export type MapView<Options, MarkerOptions, InfoWindowOptions> = {
     center: Point | null;
     zoom: number | null;
     fitBoundsToMarkers: boolean;
     markers: Array<MarkerDefinition<MarkerOptions, InfoWindowOptions>>;
-    polygons: Array<PolygonDefinition<PolygonOptions, InfoWindowOptions>>;
     options: Options;
 };
 export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
@@ -18,13 +17,6 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
     rawOptions?: MarkerOptions;
     extra: Record<string, unknown>;
 };
-export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = {
-    infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
-    points: Array<Point>;
-    title: string | null;
-    rawOptions?: PolygonOptions;
-    extra: Record<string, unknown>;
-};
 export type InfoWindowDefinition<InfoWindowOptions> = {
     headerContent: string | null;
     content: string | null;
@@ -34,16 +26,15 @@ export type InfoWindowDefinition<InfoWindowOptions> = {
     rawOptions?: InfoWindowOptions;
     extra: Record<string, unknown>;
 };
-export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindowOptions, InfoWindow, PolygonOptions, Polygon> extends Controller<HTMLElement> {
+export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindowOptions, InfoWindow> extends Controller<HTMLElement> {
     static values: {
         providerOptions: ObjectConstructor;
         view: ObjectConstructor;
     };
-    viewValue: MapView<MapOptions, MarkerOptions, InfoWindowOptions, PolygonOptions>;
+    viewValue: MapView<MapOptions, MarkerOptions, InfoWindowOptions>;
     protected map: Map;
     protected markers: Array<Marker>;
     protected infoWindows: Array<InfoWindow>;
-    protected polygons: Array<Polygon>;
     connect(): void;
     protected abstract doCreateMap({ center, zoom, options, }: {
         center: Point | null;
@@ -51,19 +42,14 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
         options: MapOptions;
     }): Map;
     createMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
-    createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
     protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
-    protected abstract doCreatePolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
-    protected createInfoWindow({ definition, element, }: {
-        definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'] | PolygonDefinition<PolygonOptions, InfoWindowOptions>['infoWindow'];
-        element: Marker | Polygon;
+    protected createInfoWindow({ definition, marker, }: {
+        definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'];
+        marker: Marker;
     }): InfoWindow;
-    protected abstract doCreateInfoWindow({ definition, element, }: {
+    protected abstract doCreateInfoWindow({ definition, marker, }: {
         definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'];
-        element: Marker;
-    } | {
-        definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>['infoWindow'];
-        element: Polygon;
+        marker: Marker;
     }): InfoWindow;
     protected abstract doFitBoundsToMarkers(): void;
     protected abstract dispatchEvent(name: string, payload: Record<string, unknown>): void;
diff --git a/src/Map/assets/dist/abstract_map_controller.js b/src/Map/assets/dist/abstract_map_controller.js
index 83cc772e..9d2e3024 100644
--- a/src/Map/assets/dist/abstract_map_controller.js
+++ b/src/Map/assets/dist/abstract_map_controller.js
@@ -5,21 +5,18 @@ class default_1 extends Controller {
         super(...arguments);
         this.markers = [];
         this.infoWindows = [];
-        this.polygons = [];
     }
     connect() {
-        const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue;
+        const { center, zoom, options, markers, fitBoundsToMarkers } = this.viewValue;
         this.dispatchEvent('pre-connect', { options });
         this.map = this.doCreateMap({ center, zoom, options });
         markers.forEach((marker) => this.createMarker(marker));
-        polygons.forEach((polygon) => this.createPolygon(polygon));
         if (fitBoundsToMarkers) {
             this.doFitBoundsToMarkers();
         }
         this.dispatchEvent('connect', {
             map: this.map,
             markers: this.markers,
-            polygons: this.polygons,
             infoWindows: this.infoWindows,
         });
     }
@@ -30,17 +27,10 @@ class default_1 extends Controller {
         this.markers.push(marker);
         return marker;
     }
-    createPolygon(definition) {
-        this.dispatchEvent('polygon:before-create', { definition });
-        const polygon = this.doCreatePolygon(definition);
-        this.dispatchEvent('polygon:after-create', { polygon });
-        this.polygons.push(polygon);
-        return polygon;
-    }
-    createInfoWindow({ definition, element, }) {
-        this.dispatchEvent('info-window:before-create', { definition, element });
-        const infoWindow = this.doCreateInfoWindow({ definition, element });
-        this.dispatchEvent('info-window:after-create', { infoWindow, element });
+    createInfoWindow({ definition, marker, }) {
+        this.dispatchEvent('info-window:before-create', { definition, marker });
+        const infoWindow = this.doCreateInfoWindow({ definition, marker });
+        this.dispatchEvent('info-window:after-create', { infoWindow, marker });
         this.infoWindows.push(infoWindow);
         return infoWindow;
     }
diff --git a/src/Map/assets/package.json b/src/Map/assets/package.json
index 4cd6dd52..2561ef55 100644
--- a/src/Map/assets/package.json
+++ b/src/Map/assets/package.json
@@ -8,7 +8,8 @@
     "types": "dist/abstract_map_controller.d.ts",
     "symfony": {
         "importmap": {
-            "@hotwired/stimulus": "^3.0.0"
+            "@hotwired/stimulus": "^3.0.0",
+            "@symfony/ux-map/abstract-map-controller": "path:%PACKAGE%/dist/abstract_map_controller.js"
         }
     },
     "peerDependencies": {
diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts
index bae763cc..802b6477 100644
--- a/src/Map/assets/src/abstract_map_controller.ts
+++ b/src/Map/assets/src/abstract_map_controller.ts
@@ -2,12 +2,11 @@ import { Controller } from '@hotwired/stimulus';
 
 export type Point = { lat: number; lng: number };
 
-export type MapView<Options, MarkerOptions, InfoWindowOptions, PolygonOptions> = {
+export type MapView<Options, MarkerOptions, InfoWindowOptions> = {
     center: Point | null;
     zoom: number | null;
     fitBoundsToMarkers: boolean;
     markers: Array<MarkerDefinition<MarkerOptions, InfoWindowOptions>>;
-    polygons: Array<PolygonDefinition<PolygonOptions, InfoWindowOptions>>;
     options: Options;
 };
 
@@ -28,14 +27,6 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
     extra: Record<string, unknown>;
 };
 
-export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = {
-    infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
-    points: Array<Point>;
-    title: string | null;
-    rawOptions?: PolygonOptions;
-    extra: Record<string, unknown>;
-};
-
 export type InfoWindowDefinition<InfoWindowOptions> = {
     headerContent: string | null;
     content: string | null;
@@ -63,23 +54,20 @@ export default abstract class<
     Marker,
     InfoWindowOptions,
     InfoWindow,
-    PolygonOptions,
-    Polygon,
 > extends Controller<HTMLElement> {
     static values = {
         providerOptions: Object,
         view: Object,
     };
 
-    declare viewValue: MapView<MapOptions, MarkerOptions, InfoWindowOptions, PolygonOptions>;
+    declare viewValue: MapView<MapOptions, MarkerOptions, InfoWindowOptions>;
 
     protected map: Map;
     protected markers: Array<Marker> = [];
     protected infoWindows: Array<InfoWindow> = [];
-    protected polygons: Array<Polygon> = [];
 
     connect() {
-        const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue;
+        const { center, zoom, options, markers, fitBoundsToMarkers } = this.viewValue;
 
         this.dispatchEvent('pre-connect', { options });
 
@@ -87,8 +75,6 @@ export default abstract class<
 
         markers.forEach((marker) => this.createMarker(marker));
 
-        polygons.forEach((polygon) => this.createPolygon(polygon));
-
         if (fitBoundsToMarkers) {
             this.doFitBoundsToMarkers();
         }
@@ -96,7 +82,6 @@ export default abstract class<
         this.dispatchEvent('connect', {
             map: this.map,
             markers: this.markers,
-            polygons: this.polygons,
             infoWindows: this.infoWindows,
         });
     }
@@ -121,29 +106,18 @@ export default abstract class<
         return marker;
     }
 
-    createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon {
-        this.dispatchEvent('polygon:before-create', { definition });
-        const polygon = this.doCreatePolygon(definition);
-        this.dispatchEvent('polygon:after-create', { polygon });
-        this.polygons.push(polygon);
-        return polygon;
-    }
-
     protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
-    protected abstract doCreatePolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
 
     protected createInfoWindow({
         definition,
-        element,
+        marker,
     }: {
-        definition:
-            | MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow']
-            | PolygonDefinition<PolygonOptions, InfoWindowOptions>['infoWindow'];
-        element: Marker | Polygon;
+        definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'];
+        marker: Marker;
     }): InfoWindow {
-        this.dispatchEvent('info-window:before-create', { definition, element });
-        const infoWindow = this.doCreateInfoWindow({ definition, element });
-        this.dispatchEvent('info-window:after-create', { infoWindow, element });
+        this.dispatchEvent('info-window:before-create', { definition, marker });
+        const infoWindow = this.doCreateInfoWindow({ definition, marker });
+        this.dispatchEvent('info-window:after-create', { infoWindow, marker });
 
         this.infoWindows.push(infoWindow);
 
@@ -152,16 +126,11 @@ export default abstract class<
 
     protected abstract doCreateInfoWindow({
         definition,
-        element,
-    }:
-        | {
-              definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'];
-              element: Marker;
-          }
-        | {
-              definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>['infoWindow'];
-              element: Polygon;
-          }): InfoWindow;
+        marker,
+    }: {
+        definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'];
+        marker: Marker;
+    }): InfoWindow;
 
     protected abstract doFitBoundsToMarkers(): void;
 
diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst
index be16614c..8231ea46 100644
--- a/src/Map/doc/index.rst
+++ b/src/Map/doc/index.rst
@@ -118,21 +118,8 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a
                     ),
                 )
             ;
-
-            // 3. You can also add Polygons, which represents an area enclosed by a series of `Point` instances
-            $map->addPolygon(new Polygon(
-                points: [
-                    new Point(48.8566, 2.3522),
-                    new Point(45.7640, 4.8357),
-                    new Point(43.2965, 5.3698),
-                    new Point(44.8378, -0.5792),
-                ],
-                infoWindow: new InfoWindow(
-                    content: 'Paris, Lyon, Marseille, Bordeaux',
-                ),
-            ));
-
-            // 4. And inject the map in your template to render it
+    
+            // 3. And inject the map in your template to render it
             return $this->render('contact/index.html.twig', [
                 'my_map' => $myMap,
             ]);
@@ -246,5 +233,5 @@ https://symfony.com/doc/current/contributing/code/bc.html
 .. _StimulusBundle configured in your app: https://symfony.com/bundles/StimulusBundle/current/index.html
 .. _`Google Maps`: https://github.com/symfony/ux-google-map
 .. _`Leaflet`: https://github.com/symfony/ux-leaflet-map
-.. _`Symfony UX Map Google Maps brige docs`: https://github.com/symfony/ux/blob/2.x/src/Map/src/Bridge/Google/README.md
-.. _`Symfony UX Map Leaflet bridge docs`: https://github.com/symfony/ux/blob/2.x/src/Map/src/Bridge/Leaflet/README.md
+.. _`Symfony UX Map Google Maps brige docs`: https://github.com/symfony/symfony-ux/blob/{version}/src/Map/src/Bridge/Google/README.md
+.. _`Symfony UX Map Leaflet bridge docs`: https://github.com/symfony/symfony-ux/blob/{version}/src/Map/src/Bridge/Leaflet/README.md
diff --git a/src/Map/src/Bridge/Google/CHANGELOG.md b/src/Map/src/Bridge/Google/CHANGELOG.md
index 46f7d49c..1d4c12f8 100644
--- a/src/Map/src/Bridge/Google/CHANGELOG.md
+++ b/src/Map/src/Bridge/Google/CHANGELOG.md
@@ -1,12 +1,5 @@
 # CHANGELOG
 
-## 2.20
-
-### BC Breaks
-
--   Renamed importmap entry `@symfony/ux-google-map/map-controller` to `@symfony/ux-google-map`, 
-    you will need to update your importmap.
-
 ## 2.19
 
 -   Bridge added
diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts
index 5095762f..aef508dc 100644
--- a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts
+++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts
@@ -1,8 +1,8 @@
-import AbstractMapController from '@symfony/ux-map';
-import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map';
+import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
+import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
 import type { LoaderOptions } from '@googlemaps/js-api-loader';
 type MapOptions = Pick<google.maps.MapOptions, 'mapId' | 'gestureHandling' | 'backgroundColor' | 'disableDoubleClickZoom' | 'zoomControl' | 'zoomControlOptions' | 'mapTypeControl' | 'mapTypeControlOptions' | 'streetViewControl' | 'streetViewControlOptions' | 'fullscreenControl' | 'fullscreenControlOptions'>;
-export default class extends AbstractMapController<MapOptions, google.maps.Map, google.maps.marker.AdvancedMarkerElementOptions, google.maps.marker.AdvancedMarkerElement, google.maps.InfoWindowOptions, google.maps.InfoWindow, google.maps.PolygonOptions, google.maps.Polygon> {
+export default class extends AbstractMapController<MapOptions, google.maps.Map, google.maps.marker.AdvancedMarkerElement, google.maps.InfoWindow> {
     static values: {
         providerOptions: ObjectConstructor;
     };
@@ -15,10 +15,9 @@ export default class extends AbstractMapController<MapOptions, google.maps.Map,
         options: MapOptions;
     }): google.maps.Map;
     protected doCreateMarker(definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>): google.maps.marker.AdvancedMarkerElement;
-    protected doCreatePolygon(definition: PolygonDefinition<google.maps.Polygon, google.maps.InfoWindowOptions>): google.maps.Polygon;
-    protected doCreateInfoWindow({ definition, element, }: {
-        definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>['infoWindow'] | PolygonDefinition<google.maps.Polygon, google.maps.InfoWindowOptions>['infoWindow'];
-        element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon;
+    protected doCreateInfoWindow({ definition, marker, }: {
+        definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>['infoWindow'];
+        marker: google.maps.marker.AdvancedMarkerElement;
     }): google.maps.InfoWindow;
     private createTextOrElement;
     private closeInfoWindowsExcept;
diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js
index 30fbe283..5a741632 100644
--- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js
+++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js
@@ -1,58 +1,8 @@
-import { Controller } from '@hotwired/stimulus';
+import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
 import { Loader } from '@googlemaps/js-api-loader';
 
-let default_1$1 = class default_1 extends Controller {
-    constructor() {
-        super(...arguments);
-        this.markers = [];
-        this.infoWindows = [];
-        this.polygons = [];
-    }
-    connect() {
-        const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue;
-        this.dispatchEvent('pre-connect', { options });
-        this.map = this.doCreateMap({ center, zoom, options });
-        markers.forEach((marker) => this.createMarker(marker));
-        polygons.forEach((polygon) => this.createPolygon(polygon));
-        if (fitBoundsToMarkers) {
-            this.doFitBoundsToMarkers();
-        }
-        this.dispatchEvent('connect', {
-            map: this.map,
-            markers: this.markers,
-            polygons: this.polygons,
-            infoWindows: this.infoWindows,
-        });
-    }
-    createMarker(definition) {
-        this.dispatchEvent('marker:before-create', { definition });
-        const marker = this.doCreateMarker(definition);
-        this.dispatchEvent('marker:after-create', { marker });
-        this.markers.push(marker);
-        return marker;
-    }
-    createPolygon(definition) {
-        this.dispatchEvent('polygon:before-create', { definition });
-        const polygon = this.doCreatePolygon(definition);
-        this.dispatchEvent('polygon:after-create', { polygon });
-        this.polygons.push(polygon);
-        return polygon;
-    }
-    createInfoWindow({ definition, element, }) {
-        this.dispatchEvent('info-window:before-create', { definition, element });
-        const infoWindow = this.doCreateInfoWindow({ definition, element });
-        this.dispatchEvent('info-window:after-create', { infoWindow, element });
-        this.infoWindows.push(infoWindow);
-        return infoWindow;
-    }
-};
-default_1$1.values = {
-    providerOptions: Object,
-    view: Object,
-};
-
 let _google;
-class default_1 extends default_1$1 {
+class default_1 extends AbstractMapController {
     async connect() {
         if (!_google) {
             _google = { maps: {} };
@@ -102,26 +52,11 @@ class default_1 extends default_1$1 {
             map: this.map,
         });
         if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: marker });
+            this.createInfoWindow({ definition: infoWindow, marker });
         }
         return marker;
     }
-    doCreatePolygon(definition) {
-        const { points, title, infoWindow, rawOptions = {} } = definition;
-        const polygon = new _google.maps.Polygon({
-            ...rawOptions,
-            paths: points,
-            map: this.map,
-        });
-        if (title) {
-            polygon.set('title', title);
-        }
-        if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: polygon });
-        }
-        return polygon;
-    }
-    doCreateInfoWindow({ definition, element, }) {
+    doCreateInfoWindow({ definition, marker, }) {
         const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
         const infoWindow = new _google.maps.InfoWindow({
             headerContent: this.createTextOrElement(headerContent),
@@ -129,34 +64,22 @@ class default_1 extends default_1$1 {
             ...otherOptions,
             ...rawOptions,
         });
-        if (element instanceof google.maps.marker.AdvancedMarkerElement) {
-            element.addListener('click', () => {
-                if (definition.autoClose) {
-                    this.closeInfoWindowsExcept(infoWindow);
-                }
-                infoWindow.open({ map: this.map, anchor: element });
+        if (definition.opened) {
+            infoWindow.open({
+                map: this.map,
+                shouldFocus: false,
+                anchor: marker,
             });
-            if (definition.opened) {
-                infoWindow.open({ map: this.map, anchor: element });
-            }
         }
-        else if (element instanceof google.maps.Polygon) {
-            element.addListener('click', (event) => {
-                if (definition.autoClose) {
-                    this.closeInfoWindowsExcept(infoWindow);
-                }
-                infoWindow.setPosition(event.latLng);
-                infoWindow.open(this.map);
-            });
-            if (definition.opened) {
-                const bounds = new google.maps.LatLngBounds();
-                element.getPath().forEach((point) => {
-                    bounds.extend(point);
-                });
-                infoWindow.setPosition(bounds.getCenter());
-                infoWindow.open({ map: this.map, anchor: element });
+        marker.addListener('click', () => {
+            if (definition.autoClose) {
+                this.closeInfoWindowsExcept(infoWindow);
             }
-        }
+            infoWindow.open({
+                map: this.map,
+                anchor: marker,
+            });
+        });
         return infoWindow;
     }
     createTextOrElement(content) {
diff --git a/src/Map/src/Bridge/Google/assets/package.json b/src/Map/src/Bridge/Google/assets/package.json
index 8b8dfdbc..e5f5ea74 100644
--- a/src/Map/src/Bridge/Google/assets/package.json
+++ b/src/Map/src/Bridge/Google/assets/package.json
@@ -18,7 +18,7 @@
         "importmap": {
             "@hotwired/stimulus": "^3.0.0",
             "@googlemaps/js-api-loader": "^1.16.6",
-            "@symfony/ux-google-map": "path:%PACKAGE%/dist/map_controller.js"
+            "@symfony/ux-google-map/map-controller": "path:%PACKAGE%/dist/map_controller.js"
         }
     },
     "peerDependencies": {
diff --git a/src/Map/src/Bridge/Google/assets/src/map_controller.ts b/src/Map/src/Bridge/Google/assets/src/map_controller.ts
index 05116d80..1e1a1a06 100644
--- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts
+++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts
@@ -7,8 +7,8 @@
  * file that was distributed with this source code.
  */
 
-import AbstractMapController from '@symfony/ux-map';
-import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map';
+import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
+import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
 import type { LoaderOptions } from '@googlemaps/js-api-loader';
 import { Loader } from '@googlemaps/js-api-loader';
 
@@ -33,12 +33,8 @@ let _google: typeof google;
 export default class extends AbstractMapController<
     MapOptions,
     google.maps.Map,
-    google.maps.marker.AdvancedMarkerElementOptions,
     google.maps.marker.AdvancedMarkerElement,
-    google.maps.InfoWindowOptions,
-    google.maps.InfoWindow,
-    google.maps.PolygonOptions,
-    google.maps.Polygon
+    google.maps.InfoWindow
 > {
     static values = {
         providerOptions: Object,
@@ -125,45 +121,21 @@ export default class extends AbstractMapController<
         });
 
         if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: marker });
+            this.createInfoWindow({ definition: infoWindow, marker });
         }
 
         return marker;
     }
 
-    protected doCreatePolygon(
-        definition: PolygonDefinition<google.maps.Polygon, google.maps.InfoWindowOptions>
-    ): google.maps.Polygon {
-        const { points, title, infoWindow, rawOptions = {} } = definition;
-
-        const polygon = new _google.maps.Polygon({
-            ...rawOptions,
-            paths: points,
-            map: this.map,
-        });
-
-        if (title) {
-            polygon.set('title', title);
-        }
-
-        if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: polygon });
-        }
-
-        return polygon;
-    }
-
     protected doCreateInfoWindow({
         definition,
-        element,
+        marker,
     }: {
-        definition:
-            | MarkerDefinition<
-                  google.maps.marker.AdvancedMarkerElementOptions,
-                  google.maps.InfoWindowOptions
-              >['infoWindow']
-            | PolygonDefinition<google.maps.Polygon, google.maps.InfoWindowOptions>['infoWindow'];
-        element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon;
+        definition: MarkerDefinition<
+            google.maps.marker.AdvancedMarkerElementOptions,
+            google.maps.InfoWindowOptions
+        >['infoWindow'];
+        marker: google.maps.marker.AdvancedMarkerElement;
     }): google.maps.InfoWindow {
         const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
 
@@ -174,35 +146,24 @@ export default class extends AbstractMapController<
             ...rawOptions,
         });
 
-        if (element instanceof google.maps.marker.AdvancedMarkerElement) {
-            element.addListener('click', () => {
-                if (definition.autoClose) {
-                    this.closeInfoWindowsExcept(infoWindow);
-                }
-                infoWindow.open({ map: this.map, anchor: element });
+        if (definition.opened) {
+            infoWindow.open({
+                map: this.map,
+                shouldFocus: false,
+                anchor: marker,
             });
+        }
 
-            if (definition.opened) {
-                infoWindow.open({ map: this.map, anchor: element });
+        marker.addListener('click', () => {
+            if (definition.autoClose) {
+                this.closeInfoWindowsExcept(infoWindow);
             }
-        } else if (element instanceof google.maps.Polygon) {
-            element.addListener('click', (event: any) => {
-                if (definition.autoClose) {
-                    this.closeInfoWindowsExcept(infoWindow);
-                }
-                infoWindow.setPosition(event.latLng);
-                infoWindow.open(this.map);
-            });
 
-            if (definition.opened) {
-                const bounds = new google.maps.LatLngBounds();
-                element.getPath().forEach((point: google.maps.LatLng) => {
-                    bounds.extend(point);
-                });
-                infoWindow.setPosition(bounds.getCenter());
-                infoWindow.open({ map: this.map, anchor: element });
-            }
-        }
+            infoWindow.open({
+                map: this.map,
+                anchor: marker,
+            });
+        });
 
         return infoWindow;
     }
diff --git a/src/Map/src/Bridge/Leaflet/CHANGELOG.md b/src/Map/src/Bridge/Leaflet/CHANGELOG.md
index e380bd8e..1d4c12f8 100644
--- a/src/Map/src/Bridge/Leaflet/CHANGELOG.md
+++ b/src/Map/src/Bridge/Leaflet/CHANGELOG.md
@@ -1,12 +1,5 @@
 # CHANGELOG
 
-## 2.20
-
-### BC Breaks
-
--   Renamed importmap entry `@symfony/ux-leaflet-map/map-controller` to `@symfony/ux-leaflet-map`, 
-    you will need to update your importmap. 
-
 ## 2.19
 
 -   Bridge added
diff --git a/src/Map/src/Bridge/Leaflet/README.md b/src/Map/src/Bridge/Leaflet/README.md
index a267776d..9618f9ba 100644
--- a/src/Map/src/Bridge/Leaflet/README.md
+++ b/src/Map/src/Bridge/Leaflet/README.md
@@ -90,34 +90,6 @@ export default class extends Controller
 }
 ```
 
-## Known issues
-
-### Unable to find `leaflet/dist/leaflet.min.css` file when using Webpack Encore
-
-When using Webpack Encore with the Leaflet bridge, you may encounter the following error:
-```
-Module build failed: Module not found:
-"./node_modules/.pnpm/file+vendor+symfony+ux-leaflet-map+assets_@hotwired+stimulus@3.0.0_leaflet@1.9.4/node_modules/@symfony/ux-leaflet-map/dist/map_controller.js" contains a reference to the file "leaflet/dist/leaflet.min.css".
-This file can not be found, please check it for typos or update it if the file got moved.
-
-Entrypoint app = runtime.67292354.js 488.0777101a.js app.b75294ae.css app.0975a86d.js
-webpack compiled with 1 error
- ELIFECYCLE  Command failed with exit code 1.
-```
-
-That's because the Leaflet's Stimulus controller references the `leaflet/dist/leaflet.min.css` file, 
-which exists on [jsDelivr](https://www.jsdelivr.com/package/npm/leaflet) (used by the Symfony AssetMapper component),
-but does not in the [`leaflet` npm package](https://www.npmjs.com/package/leaflet).
-The correct path is `leaflet/dist/leaflet.css`, but it is not possible to fix it because it would break compatibility 
-with the Symfony AssetMapper component.
-
-As a workaround, you can configure Webpack Encore to add an alias for the `leaflet/dist/leaflet.min.css` file:
-```js
-Encore.addAliases({
-  'leaflet/dist/leaflet.min.css': 'leaflet/dist/leaflet.css',
-})
-```
-
 ## Resources
 
 - [Documentation](https://symfony.com/bundles/ux-map/current/index.html)
diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts
index 6b32a8df..c152fb4c 100644
--- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts
+++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts
@@ -1,8 +1,8 @@
-import AbstractMapController from '@symfony/ux-map';
-import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map';
+import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
+import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
 import 'leaflet/dist/leaflet.min.css';
 import * as L from 'leaflet';
-import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions } from 'leaflet';
+import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions } from 'leaflet';
 type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
     tileLayer: {
         url: string;
@@ -10,7 +10,7 @@ type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
         options: Record<string, unknown>;
     };
 };
-export default class extends AbstractMapController<MapOptions, typeof L.Map, MarkerOptions, typeof L.Marker, PopupOptions, typeof L.Popup, PolygonOptions, typeof L.Polygon> {
+export default class extends AbstractMapController<MapOptions, typeof L.Map, MarkerOptions, typeof L.Marker, typeof L.Popup, PopupOptions> {
     connect(): void;
     protected dispatchEvent(name: string, payload?: Record<string, unknown>): void;
     protected doCreateMap({ center, zoom, options, }: {
@@ -19,10 +19,9 @@ export default class extends AbstractMapController<MapOptions, typeof L.Map, Mar
         options: MapOptions;
     }): L.Map;
     protected doCreateMarker(definition: MarkerDefinition): L.Marker;
-    protected doCreatePolygon(definition: PolygonDefinition): L.Polygon;
-    protected doCreateInfoWindow({ definition, element, }: {
-        definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'];
-        element: L.Marker | L.Polygon;
+    protected doCreateInfoWindow({ definition, marker, }: {
+        definition: MarkerDefinition['infoWindow'];
+        marker: L.Marker;
     }): L.Popup;
     protected doFitBoundsToMarkers(): void;
 }
diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js
index 6d4a1837..466d40c5 100644
--- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js
+++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js
@@ -1,58 +1,8 @@
-import { Controller } from '@hotwired/stimulus';
+import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
 import 'leaflet/dist/leaflet.min.css';
 import * as L from 'leaflet';
 
-class default_1 extends Controller {
-    constructor() {
-        super(...arguments);
-        this.markers = [];
-        this.infoWindows = [];
-        this.polygons = [];
-    }
-    connect() {
-        const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue;
-        this.dispatchEvent('pre-connect', { options });
-        this.map = this.doCreateMap({ center, zoom, options });
-        markers.forEach((marker) => this.createMarker(marker));
-        polygons.forEach((polygon) => this.createPolygon(polygon));
-        if (fitBoundsToMarkers) {
-            this.doFitBoundsToMarkers();
-        }
-        this.dispatchEvent('connect', {
-            map: this.map,
-            markers: this.markers,
-            polygons: this.polygons,
-            infoWindows: this.infoWindows,
-        });
-    }
-    createMarker(definition) {
-        this.dispatchEvent('marker:before-create', { definition });
-        const marker = this.doCreateMarker(definition);
-        this.dispatchEvent('marker:after-create', { marker });
-        this.markers.push(marker);
-        return marker;
-    }
-    createPolygon(definition) {
-        this.dispatchEvent('polygon:before-create', { definition });
-        const polygon = this.doCreatePolygon(definition);
-        this.dispatchEvent('polygon:after-create', { polygon });
-        this.polygons.push(polygon);
-        return polygon;
-    }
-    createInfoWindow({ definition, element, }) {
-        this.dispatchEvent('info-window:before-create', { definition, element });
-        const infoWindow = this.doCreateInfoWindow({ definition, element });
-        this.dispatchEvent('info-window:after-create', { infoWindow, element });
-        this.infoWindows.push(infoWindow);
-        return infoWindow;
-    }
-}
-default_1.values = {
-    providerOptions: Object,
-    view: Object,
-};
-
-class map_controller extends default_1 {
+class map_controller extends AbstractMapController {
     connect() {
         L.Marker.prototype.options.icon = L.divIcon({
             html: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linecap="round" clip-rule="evenodd" viewBox="0 0 500 820"><defs><linearGradient id="__sf_ux_map_gradient_marker_fill" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -37.57 37.57 0 416.45 541)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#126FC6"/><stop offset="1" stop-color="#4C9CD1"/></linearGradient><linearGradient id="__sf_ux_map_gradient_marker_border" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -19.05 19.05 0 414.48 522.49)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2E6C97"/><stop offset="1" stop-color="#3883B7"/></linearGradient></defs><circle cx="252.31" cy="266.24" r="83.99" fill="#fff"/><path fill="url(#__sf_ux_map_gradient_marker_fill)" stroke="url(#__sf_ux_map_gradient_marker_border)" stroke-width="1.1" d="M416.54 503.61c-6.57 0-12.04 5.7-12.04 11.87 0 2.78 1.56 6.3 2.7 8.74l9.3 17.88 9.26-17.88c1.13-2.43 2.74-5.79 2.74-8.74 0-6.18-5.38-11.87-11.96-11.87Zm0 7.16a4.69 4.69 0 1 1-.02 9.4 4.69 4.69 0 0 1 .02-9.4Z" transform="translate(-7889.1 -9807.44) scale(19.54)"/></svg>',
@@ -88,30 +38,19 @@ class map_controller extends default_1 {
         const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
         const marker = L.marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
         if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: marker });
+            this.createInfoWindow({ definition: infoWindow, marker });
         }
         return marker;
     }
-    doCreatePolygon(definition) {
-        const { points, title, infoWindow, rawOptions = {} } = definition;
-        const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map);
-        if (title) {
-            polygon.bindPopup(title);
-        }
-        if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: polygon });
-        }
-        return polygon;
-    }
-    doCreateInfoWindow({ definition, element, }) {
-        const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
-        element.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
+    doCreateInfoWindow({ definition, marker, }) {
+        const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
+        marker.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
         if (definition.opened) {
-            element.openPopup();
+            marker.openPopup();
         }
-        const popup = element.getPopup();
+        const popup = marker.getPopup();
         if (!popup) {
-            throw new Error('Unable to get the Popup associated with the element.');
+            throw new Error('Unable to get the Popup associated to the Marker, this should not happens.');
         }
         return popup;
     }
diff --git a/src/Map/src/Bridge/Leaflet/assets/package.json b/src/Map/src/Bridge/Leaflet/assets/package.json
index 3dd5663a..dc95b75a 100644
--- a/src/Map/src/Bridge/Leaflet/assets/package.json
+++ b/src/Map/src/Bridge/Leaflet/assets/package.json
@@ -18,7 +18,7 @@
         "importmap": {
             "@hotwired/stimulus": "^3.0.0",
             "leaflet": "^1.9.4",
-            "@symfony/ux-leaflet-map": "path:%PACKAGE%/dist/map_controller.js"
+            "@symfony/ux-leaflet-map/map-controller": "path:%PACKAGE%/dist/map_controller.js"
         }
     },
     "peerDependencies": {
diff --git a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts
index 12ed1f29..ec6a78dd 100644
--- a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts
+++ b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts
@@ -1,8 +1,8 @@
-import AbstractMapController from '@symfony/ux-map';
-import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map';
+import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
+import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
 import 'leaflet/dist/leaflet.min.css';
 import * as L from 'leaflet';
-import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions } from 'leaflet';
+import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions } from 'leaflet';
 
 type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
     tileLayer: { url: string; attribution: string; options: Record<string, unknown> };
@@ -13,10 +13,8 @@ export default class extends AbstractMapController<
     typeof L.Map,
     MarkerOptions,
     typeof L.Marker,
-    PopupOptions,
     typeof L.Popup,
-    PolygonOptions,
-    typeof L.Polygon
+    PopupOptions
 > {
     connect(): void {
         L.Marker.prototype.options.icon = L.divIcon({
@@ -65,48 +63,30 @@ export default class extends AbstractMapController<
         const marker = L.marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
 
         if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: marker });
+            this.createInfoWindow({ definition: infoWindow, marker });
         }
 
         return marker;
     }
 
-    protected doCreatePolygon(definition: PolygonDefinition): L.Polygon {
-        const { points, title, infoWindow, rawOptions = {} } = definition;
-
-        const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map);
-
-        if (title) {
-            polygon.bindPopup(title);
-        }
-
-        if (infoWindow) {
-            this.createInfoWindow({ definition: infoWindow, element: polygon });
-        }
-
-        return polygon;
-    }
-
     protected doCreateInfoWindow({
         definition,
-        element,
+        marker,
     }: {
-        definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'];
-        element: L.Marker | L.Polygon;
+        definition: MarkerDefinition['infoWindow'];
+        marker: L.Marker;
     }): L.Popup {
-        const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
-
-        element.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
+        const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
 
+        marker.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
         if (definition.opened) {
-            element.openPopup();
+            marker.openPopup();
         }
 
-        const popup = element.getPopup();
+        const popup = marker.getPopup();
         if (!popup) {
-            throw new Error('Unable to get the Popup associated with the element.');
+            throw new Error('Unable to get the Popup associated to the Marker, this should not happens.');
         }
-
         return popup;
     }
 
diff --git a/src/Map/src/Map.php b/src/Map/src/Map.php
index 3ab240ae..d8fdf005 100644
--- a/src/Map/src/Map.php
+++ b/src/Map/src/Map.php
@@ -30,11 +30,6 @@ final class Map
          * @var array<Marker>
          */
         private array $markers = [],
-
-        /**
-         * @var array<Polygon>
-         */
-        private array $polygons = [],
     ) {
     }
 
@@ -88,13 +83,6 @@ final class Map
         return $this;
     }
 
-    public function addPolygon(Polygon $polygon): self
-    {
-        $this->polygons[] = $polygon;
-
-        return $this;
-    }
-
     public function toArray(): array
     {
         if (!$this->fitBoundsToMarkers) {
@@ -113,7 +101,6 @@ final class Map
             'fitBoundsToMarkers' => $this->fitBoundsToMarkers,
             'options' => (object) ($this->options?->toArray() ?? []),
             'markers' => array_map(static fn (Marker $marker) => $marker->toArray(), $this->markers),
-            'polygons' => array_map(static fn (Polygon $polygon) => $polygon->toArray(), $this->polygons),
         ];
     }
 
@@ -122,7 +109,6 @@ final class Map
      *     center?: array{lat: float, lng: float},
      *     zoom?: float,
      *     markers?: list<array>,
-     *     polygons?: list<array>,
      *     fitBoundsToMarkers?: bool,
      *     options?: object,
      * } $map
@@ -147,12 +133,6 @@ final class Map
         }
         $map['markers'] = array_map(Marker::fromArray(...), $map['markers']);
 
-        $map['polygons'] ??= [];
-        if (!\is_array($map['polygons'])) {
-            throw new InvalidArgumentException('The "polygons" parameter must be an array.');
-        }
-        $map['polygons'] = array_map(Polygon::fromArray(...), $map['polygons']);
-
         return new self(...$map);
     }
 }
diff --git a/src/Map/src/Polygon.php b/src/Map/src/Polygon.php
deleted file mode 100644
index 5d474346..00000000
--- a/src/Map/src/Polygon.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\UX\Map;
-
-use Symfony\UX\Map\Exception\InvalidArgumentException;
-
-/**
- * Represents a polygon on a map.
- *
- * @author [Pierre Svgnt]
- */
-final readonly class Polygon
-{
-    /**
-     * @param array<string, mixed> $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side
-     */
-    public function __construct(
-        private array $points,
-        private ?string $title = null,
-        private ?InfoWindow $infoWindow = null,
-        private array $extra = [],
-    ) {
-    }
-
-    /**
-     * Convert the polygon to an array representation.
-     */
-    public function toArray(): array
-    {
-        return [
-            'points' => array_map(fn (Point $point) => $point->toArray(), $this->points),
-            'title' => $this->title,
-            'infoWindow' => $this->infoWindow?->toArray(),
-            'extra' => (object) $this->extra,
-        ];
-    }
-
-    /**
-     * @param array{
-     *     points: array<array{lat: float, lng: float}>,
-     *     title: string|null,
-     *     infoWindow: array<string, mixed>|null,
-     *     extra: object,
-     * } $polygon
-     *
-     * @internal
-     */
-    public static function fromArray(array $polygon): self
-    {
-        if (!isset($polygon['points'])) {
-            throw new InvalidArgumentException('The "points" parameter is required.');
-        }
-        $polygon['points'] = array_map(Point::fromArray(...), $polygon['points']);
-
-        if (isset($polygon['infoWindow'])) {
-            $polygon['infoWindow'] = InfoWindow::fromArray($polygon['infoWindow']);
-        }
-
-        return new self(...$polygon);
-    }
-}
diff --git a/src/Map/src/Twig/MapRuntime.php b/src/Map/src/Twig/MapRuntime.php
index cfb47560..62e50be8 100644
--- a/src/Map/src/Twig/MapRuntime.php
+++ b/src/Map/src/Twig/MapRuntime.php
@@ -14,7 +14,6 @@ namespace Symfony\UX\Map\Twig;
 use Symfony\UX\Map\Map;
 use Symfony\UX\Map\Marker;
 use Symfony\UX\Map\Point;
-use Symfony\UX\Map\Polygon;
 use Symfony\UX\Map\Renderer\RendererInterface;
 use Twig\Extension\RuntimeExtensionInterface;
 
@@ -33,13 +32,11 @@ final class MapRuntime implements RuntimeExtensionInterface
     /**
      * @param array<string, mixed> $attributes
      * @param array<string, mixed> $markers
-     * @param array<string, mixed> $polygons
      */
     public function renderMap(
         ?Map $map = null,
         array $attributes = [],
         ?array $markers = null,
-        ?array $polygons = null,
         ?array $center = null,
         ?float $zoom = null,
     ): string {
@@ -55,9 +52,6 @@ final class MapRuntime implements RuntimeExtensionInterface
         foreach ($markers ?? [] as $marker) {
             $map->addMarker(Marker::fromArray($marker));
         }
-        foreach ($polygons ?? [] as $polygons) {
-            $map->addPolygon(Polygon::fromArray($polygons));
-        }
         if (null !== $center) {
             $map->center(Point::fromArray($center));
         }
diff --git a/src/Map/src/Twig/UXMapComponent.php b/src/Map/src/Twig/UXMapComponent.php
index 39e362b3..94cb6407 100644
--- a/src/Map/src/Twig/UXMapComponent.php
+++ b/src/Map/src/Twig/UXMapComponent.php
@@ -13,7 +13,6 @@ namespace Symfony\UX\Map\Twig;
 
 use Symfony\UX\Map\Marker;
 use Symfony\UX\Map\Point;
-use Symfony\UX\Map\Polygon;
 
 /**
  * @author Simon André <smn.andre@gmail.com>
@@ -30,9 +29,4 @@ final class UXMapComponent
      * @var Marker[]
      */
     public array $markers;
-
-    /**
-     * @var Polygon[]
-     */
-    public array $polygons;
 }
diff --git a/src/Map/src/Twig/UXMapComponentListener.php b/src/Map/src/Twig/UXMapComponentListener.php
index 51034c53..3a36e558 100644
--- a/src/Map/src/Twig/UXMapComponentListener.php
+++ b/src/Map/src/Twig/UXMapComponentListener.php
@@ -32,7 +32,7 @@ final class UXMapComponentListener
         }
 
         $attributes = $event->getInputProps();
-        $map = array_intersect_key($attributes, ['markers' => 0, 'polygons' => 0, 'center' => 1, 'zoom' => 2]);
+        $map = array_intersect_key($attributes, ['markers' => 0, 'center' => 1, 'zoom' => 2]);
         $attributes = array_diff_key($attributes, $map);
 
         $html = $this->mapRuntime->renderMap(...$map, attributes: $attributes);
diff --git a/src/StimulusBundle/src/Dto/StimulusAttributes.php b/src/StimulusBundle/src/Dto/StimulusAttributes.php
index 5b91589a..1b477172 100644
--- a/src/StimulusBundle/src/Dto/StimulusAttributes.php
+++ b/src/StimulusBundle/src/Dto/StimulusAttributes.php
@@ -107,36 +107,54 @@ class StimulusAttributes implements \Stringable, \IteratorAggregate
 
     public function __toString(): string
     {
-        $attributes = [];
-
-        if ($this->controllers) {
-            $attributes[] = 'data-controller="'.$this->escape(implode(' ', $this->controllers)).'"';
-        }
+        $controllers = array_map(function (string $controllerName): string {
+            return $this->escapeAsHtmlAttr($controllerName);
+        }, $this->controllers);
 
-        if ($this->actions) {
-            $actions = [];
-            foreach ($this->actions as ['controllerName' => $controllerName, 'actionName' => $actionName, 'eventName' => $eventName]) {
-                $action = $this->escape($controllerName.'#'.$actionName);
-                if (null !== $eventName) {
-                    // done separately so we can escape, but avoid escaping ->
-                    $action = $this->escape($eventName).'->'.$action;
-                }
+        // done separately so we can escape, but avoid escaping ->
+        $actions = array_map(function (array $actionData): string {
+            $controllerName = $this->escapeAsHtmlAttr($actionData['controllerName']);
+            $actionName = $this->escapeAsHtmlAttr($actionData['actionName']);
+            $eventName = $actionData['eventName'];
 
-                $actions[] = $action;
+            $action = $controllerName.'#'.$actionName;
+            if (null !== $eventName) {
+                $action = $this->escapeAsHtmlAttr($eventName).'->'.$action;
             }
 
-            $attributes[] = 'data-action="'.implode(' ', $actions).'"';
+            return $action;
+        }, $this->actions);
+
+        $targets = [];
+        foreach ($this->targets as $key => $targetNamesString) {
+            $targetNames = explode(' ', $targetNamesString);
+            $targets[$key] = implode(' ', array_map(function (string $targetName): string {
+                return $this->escapeAsHtmlAttr($targetName);
+            }, $targetNames));
+        }
+
+        $attributes = [];
+
+        if ($controllers) {
+            $attributes[] = \sprintf('data-controller="%s"', implode(' ', $controllers));
         }
 
-        foreach ($this->targets as $k => $v) {
-            $attributes[] = $this->escape($k, 'html_attr').'="'.$this->escape($v).'"';
+        if ($actions) {
+            $attributes[] = \sprintf('data-action="%s"', implode(' ', $actions));
         }
 
-        foreach ($this->attributes as $k => $v) {
-            $attributes[] = $this->escape($k, 'html_attr').'="'.$this->escape($v).'"';
+        if ($targets) {
+            $attributes[] = implode(' ', array_map(function (string $key, string $value): string {
+                return \sprintf('%s="%s"', $key, $value);
+            }, array_keys($targets), $targets));
         }
 
-        return implode(' ', $attributes);
+        return rtrim(implode(' ', [
+            ...$attributes,
+            ...array_map(function (string $attribute, string $value): string {
+                return $attribute.'="'.$this->escapeAsHtmlAttr($value).'"';
+            }, array_keys($this->attributes), $this->attributes),
+        ]));
     }
 
     public function toArray(): array
@@ -175,7 +193,7 @@ class StimulusAttributes implements \Stringable, \IteratorAggregate
     {
         $escaped = [];
         foreach ($this->toArray() as $key => $value) {
-            $escaped[$key] = $this->escape($value);
+            $escaped[$key] = $this->escapeAsHtmlAttr($value);
         }
 
         return $escaped;
@@ -194,18 +212,18 @@ class StimulusAttributes implements \Stringable, \IteratorAggregate
         return (string) $value;
     }
 
-    private function escape(mixed $value, string $strategy = 'html'): string
+    private function escapeAsHtmlAttr(mixed $value): string
     {
         if (class_exists(EscaperRuntime::class)) {
-            return $this->env->getRuntime(EscaperRuntime::class)->escape($value, $strategy);
+            return $this->env->getRuntime(EscaperRuntime::class)->escape($value, 'html_attr');
         }
 
         if (method_exists(EscaperExtension::class, 'escape')) {
-            return EscaperExtension::escape($this->env, $value, $strategy);
+            return EscaperExtension::escape($this->env, $value, 'html_attr');
         }
 
         // since twig/twig 3.9.0: Using the internal "twig_escape_filter" function is deprecated.
-        return (string) twig_escape_filter($this->env, $value, $strategy);
+        return (string) twig_escape_filter($this->env, $value, 'html_attr');
     }
 
     /**
diff --git a/src/Translator/CHANGELOG.md b/src/Translator/CHANGELOG.md
index 789af7f2..b8d13254 100644
--- a/src/Translator/CHANGELOG.md
+++ b/src/Translator/CHANGELOG.md
@@ -1,9 +1,5 @@
 # CHANGELOG
 
-## 2.20.0
-
--   Add `throwWhenNotFound` function to configure the behavior when a translation is not found.
-
 ## 2.19.0
 
 -   Add configuration to filter dumped translations by domain.
diff --git a/src/Translator/assets/dist/translator.d.ts b/src/Translator/assets/dist/translator.d.ts
index 8d9f73e0..70da6bb4 100644
--- a/src/Translator/assets/dist/translator.d.ts
+++ b/src/Translator/assets/dist/translator.d.ts
@@ -21,7 +21,6 @@ export interface Message<Translations extends TranslationsType, Locale extends L
 }
 export declare function setLocale(locale: LocaleType | null): void;
 export declare function getLocale(): LocaleType;
-export declare function throwWhenNotFound(enabled: boolean): void;
 export declare function setLocaleFallbacks(localeFallbacks: Record<LocaleType, LocaleType>): void;
 export declare function getLocaleFallbacks(): Record<LocaleType, LocaleType>;
 export declare function trans<M extends Message<TranslationsType, LocaleType>, D extends DomainsOf<M>, P extends ParametersOf<M, D>>(...args: P extends NoParametersType ? [message: M, parameters?: P, domain?: RemoveIntlIcuSuffix<D>, locale?: LocaleOf<M>] : [message: M, parameters: P, domain?: RemoveIntlIcuSuffix<D>, locale?: LocaleOf<M>]): string;
diff --git a/src/Translator/assets/dist/translator_controller.js b/src/Translator/assets/dist/translator_controller.js
index 76292583..0dbb5793 100644
--- a/src/Translator/assets/dist/translator_controller.js
+++ b/src/Translator/assets/dist/translator_controller.js
@@ -220,7 +220,6 @@ function getPluralizationRule(number, locale) {
 
 let _locale = null;
 let _localeFallbacks = {};
-let _throwWhenNotFound = false;
 function setLocale(locale) {
     _locale = locale;
 }
@@ -230,9 +229,6 @@ function getLocale() {
         document.documentElement.lang ||
         'en');
 }
-function throwWhenNotFound(enabled) {
-    _throwWhenNotFound = enabled;
-}
 function setLocaleFallbacks(localeFallbacks) {
     _localeFallbacks = localeFallbacks;
 }
@@ -274,10 +270,7 @@ function trans(message, parameters = {}, domain = 'messages', locale = null) {
             return format(translations[locale], parameters, locale);
         }
     }
-    if (_throwWhenNotFound) {
-        throw new Error(`No translation message found with id "${message.id}".`);
-    }
     return message.id;
 }
 
-export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans };
+export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, trans };
diff --git a/src/Translator/assets/src/translator.ts b/src/Translator/assets/src/translator.ts
index 0da20f53..78e45646 100644
--- a/src/Translator/assets/src/translator.ts
+++ b/src/Translator/assets/src/translator.ts
@@ -38,7 +38,6 @@ import { format } from './formatters/formatter';
 
 let _locale: LocaleType | null = null;
 let _localeFallbacks: Record<LocaleType, LocaleType> = {};
-let _throwWhenNotFound = false;
 
 export function setLocale(locale: LocaleType | null) {
     _locale = locale;
@@ -53,10 +52,6 @@ export function getLocale(): LocaleType {
     );
 }
 
-export function throwWhenNotFound(enabled: boolean): void {
-    _throwWhenNotFound = enabled;
-}
-
 export function setLocaleFallbacks(localeFallbacks: Record<LocaleType, LocaleType>): void {
     _localeFallbacks = localeFallbacks;
 }
@@ -167,9 +162,5 @@ export function trans<
         }
     }
 
-    if (_throwWhenNotFound) {
-        throw new Error(`No translation message found with id "${message.id}".`);
-    }
-
     return message.id;
 }
diff --git a/src/Translator/doc/index.rst b/src/Translator/doc/index.rst
index 6d194ce4..614f4799 100644
--- a/src/Translator/doc/index.rst
+++ b/src/Translator/doc/index.rst
@@ -98,27 +98,6 @@ By default, the default locale is ``en`` (English) that you can configure throug
 #. Or with ``<html data-symfony-ux-translator-locale="your-locale">`` attribute
 #. Or with ``<html lang="your-locale">`` attribute
 
-Detecting missing translations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-By default, the translator will return the translation key if the translation is missing.
-
-You can change this behavior by calling ``throwWhenNotFound(true)``:
-
-.. code-block:: diff
-
-      // assets/translator.js
-
-    - import { trans, getLocale, setLocale, setLocaleFallbacks } from '@symfony/ux-translator';
-    + import { trans, getLocale, setLocale, setLocaleFallbacks, throwWhenNotFound } from '@symfony/ux-translator';
-      import { localeFallbacks } from '../var/translations/configuration';
-
-      setLocaleFallbacks(localeFallbacks);
-    + throwWhenNotFound(true)
-
-      export { trans }
-      export * from '../var/translations';
-
 Importing and using translations
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/src/TwigComponent/src/ComponentFactory.php b/src/TwigComponent/src/ComponentFactory.php
index 2a677d38..d797c399 100644
--- a/src/TwigComponent/src/ComponentFactory.php
+++ b/src/TwigComponent/src/ComponentFactory.php
@@ -41,20 +41,28 @@ final class ComponentFactory
 
     public function metadataFor(string $name): ComponentMetadata
     {
-        $name = $this->classMap[$name] ?? $name;
-
-        if (!$config = $this->config[$name] ?? null) {
-            if (($template = $this->componentTemplateFinder->findAnonymousComponentTemplate($name)) !== null) {
-                return new ComponentMetadata([
-                    'key' => $name,
-                    'template' => $template,
-                ]);
+        if ($config = $this->config[$name] ?? null) {
+            return new ComponentMetadata($config);
+        }
+
+        if ($template = $this->componentTemplateFinder->findAnonymousComponentTemplate($name)) {
+            $this->config[$name] = [
+                'key' => $name,
+                'template' => $template,
+            ];
+
+            return new ComponentMetadata($this->config[$name]);
+        }
+
+        if ($mappedName = $this->classMap[$name] ?? null) {
+            if ($config = $this->config[$mappedName] ?? null) {
+                return new ComponentMetadata($config);
             }
 
-            $this->throwUnknownComponentException($name);
+            throw new \InvalidArgumentException(\sprintf('Unknown component "%s".', $name));
         }
 
-        return new ComponentMetadata($config);
+        $this->throwUnknownComponentException($name);
     }
 
     /**
@@ -62,11 +70,13 @@ final class ComponentFactory
      */
     public function create(string $name, array $data = []): MountedComponent
     {
-        return $this->mountFromObject(
-            $this->getComponent($name),
-            $data,
-            $this->metadataFor($name)
-        );
+        $metadata = $this->metadataFor($name);
+
+        if ($metadata->isAnonymous()) {
+            return $this->mountFromObject(new AnonymousComponent(), $data, $metadata);
+        }
+
+        return $this->mountFromObject($this->components->get($metadata->getName()), $data, $metadata);
     }
 
     /**
@@ -101,10 +111,7 @@ final class ComponentFactory
         foreach ($data as $key => $value) {
             if ($value instanceof \Stringable) {
                 $data[$key] = (string) $value;
-                continue;
             }
-
-            $data[$key] = $value;
         }
 
         return new MountedComponent(
@@ -118,10 +125,18 @@ final class ComponentFactory
 
     /**
      * Returns the "unmounted" component.
+     *
+     * @internal
      */
     public function get(string $name): object
     {
-        return $this->getComponent($name);
+        $metadata = $this->metadataFor($name);
+
+        if ($metadata->isAnonymous()) {
+            return new AnonymousComponent();
+        }
+
+        return $this->components->get($metadata->getName());
     }
 
     private function mount(object $component, array &$data): void
@@ -159,21 +174,6 @@ final class ComponentFactory
         $component->mount(...$parameters);
     }
 
-    private function getComponent(string $name): object
-    {
-        $name = $this->classMap[$name] ?? $name;
-
-        if (!$this->components->has($name)) {
-            if ($this->isAnonymousComponent($name)) {
-                return new AnonymousComponent();
-            }
-
-            $this->throwUnknownComponentException($name);
-        }
-
-        return $this->components->get($name);
-    }
-
     private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): array
     {
         $event = new PreMountEvent($component, $data, $componentMetadata);
@@ -215,11 +215,6 @@ final class ComponentFactory
         ];
     }
 
-    private function isAnonymousComponent(string $name): bool
-    {
-        return null !== $this->componentTemplateFinder->findAnonymousComponentTemplate($name);
-    }
-
     /**
      * @return never
      */
diff --git a/src/TwigComponent/src/ComponentMetadata.php b/src/TwigComponent/src/ComponentMetadata.php
index a152417a..b71e3491 100644
--- a/src/TwigComponent/src/ComponentMetadata.php
+++ b/src/TwigComponent/src/ComponentMetadata.php
@@ -57,6 +57,11 @@ final class ComponentMetadata
         return $this->get('expose_public_props', false);
     }
 
+    public function isAnonymous(): bool
+    {
+        return !isset($this->config['service_id']);
+    }
+
     public function getAttributesVar(): string
     {
         return $this->get('attributes_var', 'attributes');
diff --git a/ux.symfony.com/importmap.php b/ux.symfony.com/importmap.php
index 90273c85..af8af902 100644
--- a/ux.symfony.com/importmap.php
+++ b/ux.symfony.com/importmap.php
@@ -190,6 +190,9 @@ return [
     'chart.js' => [
         'version' => '4.4.3',
     ],
+    '@symfony/ux-map/abstract-map-controller' => [
+        'path' => './vendor/symfony/ux-map/assets/dist/abstract_map_controller.js',
+    ],
     'leaflet' => [
         'version' => '1.9.4',
     ],
@@ -197,7 +200,7 @@ return [
         'version' => '1.9.4',
         'type' => 'css',
     ],
-    '@symfony/ux-leaflet-map' => [
+    '@symfony/ux-leaflet-map/map-controller' => [
         'path' => './vendor/symfony/ux-leaflet-map/assets/dist/map_controller.js',
     ],
 ];


Test patch:
diff --git a/src/Chartjs/tests/Twig/ChartExtensionTest.php b/src/Chartjs/tests/Twig/ChartExtensionTest.php
index 0f3ae34e..03472690 100644
--- a/src/Chartjs/tests/Twig/ChartExtensionTest.php
+++ b/src/Chartjs/tests/Twig/ChartExtensionTest.php
@@ -56,7 +56,7 @@ class ChartExtensionTest extends TestCase
         );
 
         $this->assertSame(
-            '<canvas data-controller="mycontroller symfony--ux-chartjs--chart" data-symfony--ux-chartjs--chart-view-value="{&quot;type&quot;:&quot;line&quot;,&quot;data&quot;:{&quot;labels&quot;:[&quot;January&quot;,&quot;February&quot;,&quot;March&quot;,&quot;April&quot;,&quot;May&quot;,&quot;June&quot;,&quot;July&quot;],&quot;datasets&quot;:[{&quot;label&quot;:&quot;My First dataset&quot;,&quot;backgroundColor&quot;:&quot;rgb(255, 99, 132)&quot;,&quot;borderColor&quot;:&quot;rgb(255, 99, 132)&quot;,&quot;data&quot;:[0,10,5,2,20,30,45]}]},&quot;options&quot;:{&quot;showLines&quot;:false}}" class="myclass"></canvas>',
+            '<canvas data-controller="mycontroller symfony--ux-chartjs--chart" data-symfony--ux-chartjs--chart-view-value="&#x7B;&quot;type&quot;&#x3A;&quot;line&quot;,&quot;data&quot;&#x3A;&#x7B;&quot;labels&quot;&#x3A;&#x5B;&quot;January&quot;,&quot;February&quot;,&quot;March&quot;,&quot;April&quot;,&quot;May&quot;,&quot;June&quot;,&quot;July&quot;&#x5D;,&quot;datasets&quot;&#x3A;&#x5B;&#x7B;&quot;label&quot;&#x3A;&quot;My&#x20;First&#x20;dataset&quot;,&quot;backgroundColor&quot;&#x3A;&quot;rgb&#x28;255,&#x20;99,&#x20;132&#x29;&quot;,&quot;borderColor&quot;&#x3A;&quot;rgb&#x28;255,&#x20;99,&#x20;132&#x29;&quot;,&quot;data&quot;&#x3A;&#x5B;0,10,5,2,20,30,45&#x5D;&#x7D;&#x5D;&#x7D;,&quot;options&quot;&#x3A;&#x7B;&quot;showLines&quot;&#x3A;false&#x7D;&#x7D;" class="myclass"></canvas>',
             $rendered
         );
     }
diff --git a/src/LiveComponent/tests/Unit/Twig/LiveComponentRuntimeTest.php b/src/LiveComponent/tests/Unit/Twig/LiveComponentRuntimeTest.php
index 01f067c0..30c3ba8c 100644
--- a/src/LiveComponent/tests/Unit/Twig/LiveComponentRuntimeTest.php
+++ b/src/LiveComponent/tests/Unit/Twig/LiveComponentRuntimeTest.php
@@ -28,15 +28,17 @@ final class LiveComponentRuntimeTest extends KernelTestCase
         $this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-some-prop-param="val2" data-live-action-param="action-name"', $props);
 
         $props = $runtime->liveAction('action-name', ['prop1' => 'val1', 'prop2' => 'val2'], ['debounce' => 300]);
-        $this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce(300)|action-name"', $props);
+        $this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce(300)|action-name"', html_entity_decode($props));
+        $this->assertSame('data-action="live#action" data-live-prop1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce&#x28;300&#x29;&#x7C;action-name"', $props);
 
         $props = $runtime->liveAction('action-name:prevent', ['pro1' => 'val1', 'prop2' => 'val2'], ['debounce' => 300]);
-        $this->assertSame('data-action="live#action:prevent" data-live-pro1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce(300)|action-name"', $props);
+        $this->assertSame('data-action="live#action:prevent" data-live-pro1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce(300)|action-name"', html_entity_decode($props));
+        $this->assertSame('data-action="live#action&#x3A;prevent" data-live-pro1-param="val1" data-live-prop2-param="val2" data-live-action-param="debounce&#x28;300&#x29;&#x7C;action-name"', $props);
 
         $props = $runtime->liveAction('action-name:prevent', [], ['debounce' => 300]);
-        $this->assertSame('data-action="live#action:prevent" data-live-action-param="debounce(300)|action-name"', $props);
+        $this->assertSame('data-action="live#action:prevent" data-live-action-param="debounce(300)|action-name"', html_entity_decode($props));
 
         $props = $runtime->liveAction('action-name', [], [], 'keydown.esc');
-        $this->assertSame('data-action="keydown.esc->live#action" data-live-action-param="action-name"', $props);
+        $this->assertSame('data-action="keydown.esc->live#action" data-live-action-param="action-name"', html_entity_decode($props));
     }
 }
diff --git a/src/Map/assets/test/abstract_map_controller.test.ts b/src/Map/assets/test/abstract_map_controller.test.ts
index c9e0e38a..0beadef2 100644
--- a/src/Map/assets/test/abstract_map_controller.test.ts
+++ b/src/Map/assets/test/abstract_map_controller.test.ts
@@ -20,28 +20,14 @@ class MyMapController extends AbstractMapController {
         const marker = { marker: 'marker', title: definition.title };
 
         if (definition.infoWindow) {
-            this.createInfoWindow({ definition: definition.infoWindow, element: marker });
+            this.createInfoWindow({ definition: definition.infoWindow, marker });
         }
 
         return marker;
     }
 
-    doCreatePolygon(definition) {
-        const polygon = { polygon: 'polygon', title: definition.title };
-
-        if (definition.infoWindow) {
-            this.createInfoWindow({ definition: definition.infoWindow, element: polygon });
-        }
-        return polygon;
-    }
-
-    doCreateInfoWindow({ definition, element }) {
-        if (element.marker) {
-            return { infoWindow: 'infoWindow', headerContent: definition.headerContent, marker: element.title };
-        }
-        if (element.polygon) {
-            return { infoWindow: 'infoWindow', headerContent: definition.headerContent, polygon: element.title };
-        }
+    doCreateInfoWindow({ definition, marker }) {
+        return { infoWindow: 'infoWindow', headerContent: definition.headerContent, marker: marker.title };
     }
 
     doFitBoundsToMarkers() {
@@ -61,61 +47,12 @@ describe('AbstractMapController', () => {
     beforeEach(() => {
         container = mountDOM(`
           <div 
-            data-testid="map" 
-            data-controller="map" 
-            style="height: 700px; margin: 10px;" 
-            data-map-provider-options-value="{}" 
-            data-map-view-value='{
-                "center": { "lat": 48.8566, "lng": 2.3522 },
-                "zoom": 4,
-                "fitBoundsToMarkers": true,
-                "options": {},
-                "markers": [
-                    {
-                        "position": { "lat": 48.8566, "lng": 2.3522 },
-                        "title": "Paris",
-                        "infoWindow": null
-                    },
-                    {
-                        "position": { "lat": 45.764, "lng": 4.8357 },
-                        "title": "Lyon",
-                        "infoWindow": {
-                            "headerContent": "<b>Lyon</b>",
-                            "content": "The French town in the historic Rhône-Alpes region, located at the junction of the Rhône and Saône rivers.",
-                            "position": null,
-                            "opened": false,
-                            "autoClose": true
-                        }
-                    }
-                ],
-                "polygons": [
-                    {
-                        "coordinates": [
-                            { "lat": 48.858844, "lng": 2.294351 },
-                            { "lat": 48.853, "lng": 2.3499 },
-                            { "lat": 48.8566, "lng": 2.3522 }
-                        ],
-                        "title": "Polygon 1",
-                        "infoWindow": null
-                    },
-                    {
-                        "coordinates": [
-                            { "lat": 45.764043, "lng": 4.835659 },
-                            { "lat": 45.750000, "lng": 4.850000 },
-                            { "lat": 45.770000, "lng": 4.820000 }
-                        ],
-                        "title": "Polygon 2",
-                        "infoWindow": {
-                            "headerContent": "<b>Polygon 2</b>",
-                            "content": "A polygon around Lyon with some additional info.",
-                            "position": null,
-                            "opened": false,
-                            "autoClose": true
-                        }
-                    }
-                ]
-            }'>
-        </div>
+              data-testid="map" 
+              data-controller="map" 
+              style="height&#x3A;&#x20;700px&#x3B;&#x20;margin&#x3A;&#x20;10px" 
+              data-map-provider-options-value="&#x7B;&#x7D;" 
+              data-map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;4,&quot;fitBoundsToMarkers&quot;&#x3A;true,&quot;options&quot;&#x3A;&#x7B;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;45.764,&quot;lng&quot;&#x3A;4.8357&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;&quot;&lt;b&gt;Lyon&lt;&#x5C;&#x2F;b&gt;&quot;,&quot;content&quot;&#x3A;&quot;The&#x20;French&#x20;town&#x20;in&#x20;the&#x20;historic&#x20;Rh&#x5C;u00f4ne-Alpes&#x20;region,&#x20;located&#x20;at&#x20;the&#x20;junction&#x20;of&#x20;the&#x20;Rh&#x5C;u00f4ne&#x20;and&#x20;Sa&#x5C;u00f4ne&#x20;rivers.&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;&#x7D;"
+          ></div>
         `);
     });
 
@@ -123,7 +60,7 @@ describe('AbstractMapController', () => {
         clearDOM();
     });
 
-    it('connect and create map, marker, polygon and info window', async () => {
+    it('connect and create map, marker and info window', async () => {
         const div = getByTestId(container, 'map');
         expect(div).not.toHaveClass('connected');
 
@@ -136,21 +73,12 @@ describe('AbstractMapController', () => {
             { marker: 'marker', title: 'Paris' },
             { marker: 'marker', title: 'Lyon' },
         ]);
-        expect(controller.polygons).toEqual([
-            { polygon: 'polygon', title: 'Polygon 1' },
-            { polygon: 'polygon', title: 'Polygon 2' },
-        ]);
         expect(controller.infoWindows).toEqual([
             {
                 headerContent: '<b>Lyon</b>',
                 infoWindow: 'infoWindow',
                 marker: 'Lyon',
             },
-            {
-                headerContent: '<b>Polygon 2</b>',
-                infoWindow: 'infoWindow',
-                polygon: 'Polygon 2',
-            },
         ]);
     });
 });
diff --git a/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts b/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts
index f1b08abb..1db8edff 100644
--- a/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts
+++ b/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts
@@ -41,7 +41,7 @@ describe('GoogleMapsController', () => {
               data-controller="check google" 
               style="height&#x3A;&#x20;700px&#x3B;&#x20;margin&#x3A;&#x20;10px" 
               data-google-provider-options-value="&#x7B;&quot;version&quot;&#x3A;&quot;weekly&quot;,&quot;libraries&quot;&#x3A;&#x5B;&quot;maps&quot;,&quot;marker&quot;&#x5D;,&quot;apiKey&quot;&#x3A;&quot;&quot;&#x7D;" 
-              data-google-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;4,&quot;fitBoundsToMarkers&quot;&#x3A;true,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;&quot;YOUR_MAP_ID&quot;,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControl&quot;&#x3A;true,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControl&quot;&#x3A;true,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControl&quot;&#x3A;true,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControl&quot;&#x3A;true,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;45.764,&quot;lng&quot;&#x3A;4.8357&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;&quot;&lt;b&gt;Lyon&lt;&#x5C;&#x2F;b&gt;&quot;,&quot;content&quot;&#x3A;&quot;The&#x20;French&#x20;town&#x20;in&#x20;the&#x20;historic&#x20;Rh&#x5C;u00f4ne-Alpes&#x20;region,&#x20;located&#x20;at&#x20;the&#x20;junction&#x20;of&#x20;the&#x20;Rh&#x5C;u00f4ne&#x20;and&#x20;Sa&#x5C;u00f4ne&#x20;rivers.&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;,&quot;polygons&quot;&#x3A;[]&#x7D;"
+              data-google-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;4,&quot;fitBoundsToMarkers&quot;&#x3A;true,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;&quot;YOUR_MAP_ID&quot;,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControl&quot;&#x3A;true,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControl&quot;&#x3A;true,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControl&quot;&#x3A;true,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControl&quot;&#x3A;true,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;45.764,&quot;lng&quot;&#x3A;4.8357&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;&quot;&lt;b&gt;Lyon&lt;&#x5C;&#x2F;b&gt;&quot;,&quot;content&quot;&#x3A;&quot;The&#x20;French&#x20;town&#x20;in&#x20;the&#x20;historic&#x20;Rh&#x5C;u00f4ne-Alpes&#x20;region,&#x20;located&#x20;at&#x20;the&#x20;junction&#x20;of&#x20;the&#x20;Rh&#x5C;u00f4ne&#x20;and&#x20;Sa&#x5C;u00f4ne&#x20;rivers.&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;&#x7D;"
           ></div>
         `);
     });
diff --git a/src/Map/src/Bridge/Google/assets/vitest.config.mjs b/src/Map/src/Bridge/Google/assets/vitest.config.mjs
index c957798d..dde4635d 100644
--- a/src/Map/src/Bridge/Google/assets/vitest.config.mjs
+++ b/src/Map/src/Bridge/Google/assets/vitest.config.mjs
@@ -6,7 +6,7 @@ export default mergeConfig(
     defineConfig({
         resolve: {
             alias: {
-                '@symfony/ux-map': __dirname + '/../../../../assets/src/abstract_map_controller.ts',
+                '@symfony/ux-map/abstract-map-controller': __dirname + '/../../../../assets/src/abstract_map_controller.ts',
             },
         },
         define: {
diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
index 32ca96df..db011e30 100644
--- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
+++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
@@ -29,26 +29,26 @@ class GoogleRendererTest extends RendererTestCase
             ->zoom(12);
 
         yield 'simple map, with minimum options' => [
-            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
             'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'),
             'map' => $map,
         ];
 
         yield 'with every options' => [
-            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;id&quot;&#x3A;&quot;gmap&quot;,&quot;language&quot;&#x3A;&quot;fr&quot;,&quot;region&quot;&#x3A;&quot;FR&quot;,&quot;nonce&quot;&#x3A;&quot;abcd&quot;,&quot;retries&quot;&#x3A;10,&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;maps.googleapis.com&#x5C;&#x2F;maps&#x5C;&#x2F;api&#x5C;&#x2F;js&quot;,&quot;version&quot;&#x3A;&quot;quarterly&quot;,&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;id&quot;&#x3A;&quot;gmap&quot;,&quot;language&quot;&#x3A;&quot;fr&quot;,&quot;region&quot;&#x3A;&quot;FR&quot;,&quot;nonce&quot;&#x3A;&quot;abcd&quot;,&quot;retries&quot;&#x3A;10,&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;maps.googleapis.com&#x5C;&#x2F;maps&#x5C;&#x2F;api&#x5C;&#x2F;js&quot;,&quot;version&quot;&#x3A;&quot;quarterly&quot;,&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
             'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key', id: 'gmap', language: 'fr', region: 'FR', nonce: 'abcd', retries: 10, url: 'https://maps.googleapis.com/maps/api/js', version: 'quarterly'),
             'map' => $map,
         ];
 
         yield 'with custom attributes' => [
-            'expected_render' => '<div data-controller="my-custom-controller symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;" class="map"></div>',
+            'expected_render' => '<div data-controller="my-custom-controller symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;" class="map"></div>',
             'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'),
             'map' => $map,
             'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'],
         ];
 
         yield 'with markers and infoWindows' => [
-            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x5D;&#x7D;"></div>',
             'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'),
             'map' => (clone $map)
                 ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris'))
@@ -56,7 +56,7 @@ class GoogleRendererTest extends RendererTestCase
         ];
 
         yield 'with controls enabled' => [
-            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
             'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'),
             'map' => (clone $map)
                 ->options(new GoogleOptions(
@@ -68,7 +68,7 @@ class GoogleRendererTest extends RendererTestCase
         ];
 
         yield 'without controls enabled' => [
-            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="&#x7B;&quot;apiKey&quot;&#x3A;&quot;api_key&quot;&#x7D;" data-symfony--ux-google-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;null,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
             'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'),
             'map' => (clone $map)
                 ->options(new GoogleOptions(
diff --git a/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts b/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts
index 5a51bf5f..e6aa9276 100644
--- a/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts
+++ b/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts
@@ -36,12 +36,12 @@ describe('LeafletController', () => {
 
     beforeEach(() => {
         container = mountDOM(`
-           <div 
+          <div 
               data-testid="map" 
               data-controller="check leaflet" 
               style="height&#x3A;&#x20;700px&#x3B;&#x20;margin&#x3A;&#x20;10px" 
               data-leaflet-provider-options-value="&#x7B;&#x7D;" 
-              data-leaflet-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;4,&quot;fitBoundsToMarkers&quot;&#x3A;true,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;45.764,&quot;lng&quot;&#x3A;4.8357&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;&quot;&lt;b&gt;Lyon&lt;&#x5C;&#x2F;b&gt;&quot;,&quot;content&quot;&#x3A;&quot;The&#x20;French&#x20;town&#x20;in&#x20;the&#x20;historic&#x20;Rh&#x5C;u00f4ne-Alpes&#x20;region,&#x20;located&#x20;at&#x20;the&#x20;junction&#x20;of&#x20;the&#x20;Rh&#x5C;u00f4ne&#x20;and&#x20;Sa&#x5C;u00f4ne&#x20;rivers.&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;,&quot;polygons&quot;&#x3A;[]&#x7D;"
+              data-leaflet-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;4,&quot;fitBoundsToMarkers&quot;&#x3A;true,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;45.764,&quot;lng&quot;&#x3A;4.8357&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;&quot;&lt;b&gt;Lyon&lt;&#x5C;&#x2F;b&gt;&quot;,&quot;content&quot;&#x3A;&quot;The&#x20;French&#x20;town&#x20;in&#x20;the&#x20;historic&#x20;Rh&#x5C;u00f4ne-Alpes&#x20;region,&#x20;located&#x20;at&#x20;the&#x20;junction&#x20;of&#x20;the&#x20;Rh&#x5C;u00f4ne&#x20;and&#x20;Sa&#x5C;u00f4ne&#x20;rivers.&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;&#x7D;"
           ></div>
         `);
     });
diff --git a/src/Map/src/Bridge/Leaflet/assets/vitest.config.mjs b/src/Map/src/Bridge/Leaflet/assets/vitest.config.mjs
index 8fb88b88..7072015e 100644
--- a/src/Map/src/Bridge/Leaflet/assets/vitest.config.mjs
+++ b/src/Map/src/Bridge/Leaflet/assets/vitest.config.mjs
@@ -6,7 +6,7 @@ export default mergeConfig(
     defineConfig({
         resolve: {
             alias: {
-                '@symfony/ux-map': __dirname + '/../../../../assets/src/abstract_map_controller.ts',
+                '@symfony/ux-map/abstract-map-controller': __dirname + '/../../../../assets/src/abstract_map_controller.ts',
                 'leaflet/dist/leaflet.min.css': 'leaflet/dist/leaflet.css',
             },
         },
diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php
index d9ad391c..6931f53a 100644
--- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php
+++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php
@@ -28,20 +28,20 @@ class LeafletRendererTest extends RendererTestCase
             ->zoom(12);
 
         yield 'simple map' => [
-            'expected_render' => '<div data-controller="symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
             'renderer' => new LeafletRenderer(new StimulusHelper(null)),
             'map' => $map,
         ];
 
         yield 'with custom attributes' => [
-            'expected_render' => '<div data-controller="my-custom-controller symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;" class="map"></div>',
+            'expected_render' => '<div data-controller="my-custom-controller symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x5D;&#x7D;" class="map"></div>',
             'renderer' => new LeafletRenderer(new StimulusHelper(null)),
             'map' => $map,
             'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'],
         ];
 
         yield 'with markers and infoWindows' => [
-            'expected_render' => '<div data-controller="symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x5D;,&quot;polygons&quot;&#x3A;&#x5B;&#x5D;&#x7D;"></div>',
+            'expected_render' => '<div data-controller="symfony--ux-leaflet-map--map" data-symfony--ux-leaflet-map--map-provider-options-value="&#x7B;&#x7D;" data-symfony--ux-leaflet-map--map-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;12,&quot;fitBoundsToMarkers&quot;&#x3A;false,&quot;options&quot;&#x3A;&#x7B;&quot;tileLayer&quot;&#x3A;&#x7B;&quot;url&quot;&#x3A;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;tile.openstreetmap.org&#x5C;&#x2F;&#x7B;z&#x7D;&#x5C;&#x2F;&#x7B;x&#x7D;&#x5C;&#x2F;&#x7B;y&#x7D;.png&quot;,&quot;attribution&quot;&#x3A;&quot;&#x5C;u00a9&#x20;&lt;a&#x20;href&#x3D;&#x5C;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;www.openstreetmap.org&#x5C;&#x2F;copyright&#x5C;&quot;&gt;OpenStreetMap&lt;&#x5C;&#x2F;a&gt;&quot;,&quot;options&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;null,&quot;content&quot;&#x3A;&quot;Lyon&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;,&quot;extra&quot;&#x3A;&#x7B;&#x7D;&#x7D;&#x5D;&#x7D;"></div>',
             'renderer' => new LeafletRenderer(new StimulusHelper(null)),
             'map' => (clone $map)
                 ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris'))
diff --git a/src/Map/tests/MapFactoryTest.php b/src/Map/tests/MapFactoryTest.php
index fcff3b05..a19e2d79 100644
--- a/src/Map/tests/MapFactoryTest.php
+++ b/src/Map/tests/MapFactoryTest.php
@@ -32,13 +32,6 @@ class MapFactoryTest extends TestCase
         $this->assertSame($array['markers'][0]['title'], $markers[0]['title']);
         $this->assertSame($array['markers'][0]['infoWindow']['headerContent'], $markers[0]['infoWindow']['headerContent']);
         $this->assertSame($array['markers'][0]['infoWindow']['content'], $markers[0]['infoWindow']['content']);
-
-        $this->assertCount(1, $polygons = $map->toArray()['polygons']);
-        $this->assertEquals($array['polygons'][0]['points'], $polygons[0]['points']);
-        $this->assertEquals($array['polygons'][0]['points'], $polygons[0]['points']);
-        $this->assertSame($array['polygons'][0]['title'], $polygons[0]['title']);
-        $this->assertSame($array['polygons'][0]['infoWindow']['headerContent'], $polygons[0]['infoWindow']['headerContent']);
-        $this->assertSame($array['polygons'][0]['infoWindow']['content'], $polygons[0]['infoWindow']['content']);
     }
 
     public function testFromArrayWithInvalidCenter(): void
@@ -83,30 +76,6 @@ class MapFactoryTest extends TestCase
         Map::fromArray($array);
     }
 
-    public function testFromArrayWithInvalidPolygons(): void
-    {
-        $array = self::createMapArray();
-        $array['polygons'] = 'invalid';
-
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The "polygons" parameter must be an array.');
-        Map::fromArray($array);
-    }
-
-    public function testFromArrayWithInvalidPolygon(): void
-    {
-        $array = self::createMapArray();
-        $array['polygons'] = [
-            [
-                'invalid',
-            ],
-        ];
-
-        $this->expectException(\InvalidArgumentException::class);
-        $this->expectExceptionMessage('The "points" parameter is required.');
-        Map::fromArray($array);
-    }
-
     private static function createMapArray(): array
     {
         return [
@@ -128,29 +97,6 @@ class MapFactoryTest extends TestCase
                     ],
                 ],
             ],
-            'polygons' => [
-                [
-                    'points' => [
-                        [
-                            'lat' => 48.858844,
-                            'lng' => 2.294351,
-                        ],
-                        [
-                            'lat' => 48.853,
-                            'lng' => 2.3499,
-                        ],
-                        [
-                            'lat' => 48.8566,
-                            'lng' => 2.3522,
-                        ],
-                    ],
-                    'title' => 'Polygon 1',
-                    'infoWindow' => [
-                        'headerContent' => 'Polygon 1',
-                        'content' => 'Polygon 1',
-                    ],
-                ],
-            ],
         ];
     }
 }
diff --git a/src/Map/tests/MapTest.php b/src/Map/tests/MapTest.php
index 95703724..99a4f1eb 100644
--- a/src/Map/tests/MapTest.php
+++ b/src/Map/tests/MapTest.php
@@ -18,7 +18,6 @@ use Symfony\UX\Map\Map;
 use Symfony\UX\Map\MapOptionsInterface;
 use Symfony\UX\Map\Marker;
 use Symfony\UX\Map\Point;
-use Symfony\UX\Map\Polygon;
 
 class MapTest extends TestCase
 {
@@ -56,7 +55,6 @@ class MapTest extends TestCase
             'fitBoundsToMarkers' => true,
             'options' => $array['options'],
             'markers' => [],
-            'polygons' => [],
         ], $array);
     }
 
@@ -75,7 +73,6 @@ class MapTest extends TestCase
             'fitBoundsToMarkers' => false,
             'options' => $array['options'],
             'markers' => [],
-            'polygons' => [],
         ], $array);
     }
 
@@ -108,36 +105,11 @@ class MapTest extends TestCase
                 position: new Point(43.2965, 5.3698),
                 title: 'Marseille',
                 infoWindow: new InfoWindow(headerContent: '<b>Marseille</b>', content: 'Marseille', position: new Point(43.2965, 5.3698), opened: true)
-            ))
-            ->addPolygon(new Polygon(
-                points: [
-                    new Point(48.858844, 2.294351),
-                    new Point(48.853, 2.3499),
-                    new Point(48.8566, 2.3522),
-                ],
-                title: 'Polygon 1',
-                infoWindow: null,
-            ))
-            ->addPolygon(new Polygon(
-                points: [
-                    new Point(45.764043, 4.835659),
-                    new Point(45.75, 4.85),
-                    new Point(45.77, 4.82),
-                ],
-                title: 'Polygon 2',
-                infoWindow: new InfoWindow(
-                    headerContent: '<b>Polygon 2</b>',
-                    content: 'A polygon around Lyon with some additional info.',
-                    position: new Point(45.764, 4.8357),
-                    opened: true,
-                    autoClose: true,
-                ),
-            ))
-        ;
+            ));
 
         $array = $map->toArray();
 
-        self::assertEquals([
+        self::assertSame([
             'center' => ['lat' => 48.8566, 'lng' => 2.3522],
             'zoom' => 6.0,
             'fitBoundsToMarkers' => true,
@@ -183,35 +155,6 @@ class MapTest extends TestCase
                     'extra' => $array['markers'][2]['extra'],
                 ],
             ],
-            'polygons' => [
-                [
-                    'points' => [
-                        ['lat' => 48.858844, 'lng' => 2.294351],
-                        ['lat' => 48.853, 'lng' => 2.3499],
-                        ['lat' => 48.8566, 'lng' => 2.3522],
-                    ],
-                    'title' => 'Polygon 1',
-                    'infoWindow' => null,
-                    'extra' => $array['polygons'][0]['extra'],
-                ],
-                [
-                    'points' => [
-                        ['lat' => 45.764043, 'lng' => 4.835659],
-                        ['lat' => 45.75, 'lng' => 4.85],
-                        ['lat' => 45.77, 'lng' => 4.82],
-                    ],
-                    'title' => 'Polygon 2',
-                    'infoWindow' => [
-                        'headerContent' => '<b>Polygon 2</b>',
-                        'content' => 'A polygon around Lyon with some additional info.',
-                        'position' => ['lat' => 45.764, 'lng' => 4.8357],
-                        'opened' => true,
-                        'autoClose' => true,
-                        'extra' => $array['polygons'][1]['infoWindow']['extra'],
-                    ],
-                    'extra' => $array['polygons'][1]['extra'],
-                ],
-            ],
         ], $array);
 
         self::assertSame('roadmap', $array['options']->mapTypeId);
diff --git a/src/Notify/tests/Twig/NotifyRuntimeTest.php b/src/Notify/tests/Twig/NotifyRuntimeTest.php
index 8c3b3786..7f21970f 100644
--- a/src/Notify/tests/Twig/NotifyRuntimeTest.php
+++ b/src/Notify/tests/Twig/NotifyRuntimeTest.php
@@ -39,13 +39,13 @@ class NotifyRuntimeTest extends TestCase
 
     public static function streamNotificationsDataProvider(): iterable
     {
-        $publicUrl = 'http://localhost:9090/.well-known/mercure';
+        $publicUrl = 'http&#x3A;&#x2F;&#x2F;localhost&#x3A;9090&#x2F;.well-known&#x2F;mercure';
 
         yield [
             [['/topic/1', '/topic/2']],
             '<div '.
                 'data-controller="symfony--ux-notify--notify" '.
-                'data-symfony--ux-notify--notify-topics-value="[&quot;\/topic\/1&quot;,&quot;\/topic\/2&quot;]" '.
+                'data-symfony--ux-notify--notify-topics-value="&#x5B;&quot;&#x5C;&#x2F;topic&#x5C;&#x2F;1&quot;,&quot;&#x5C;&#x2F;topic&#x5C;&#x2F;2&quot;&#x5D;" '.
                 'data-symfony--ux-notify--notify-hub-value="'.$publicUrl.'"'.
             '></div>',
         ];
@@ -54,7 +54,7 @@ class NotifyRuntimeTest extends TestCase
             ['/topic/1'],
             '<div '.
                 'data-controller="symfony--ux-notify--notify" '.
-                'data-symfony--ux-notify--notify-topics-value="[&quot;\/topic\/1&quot;]" '.
+                'data-symfony--ux-notify--notify-topics-value="&#x5B;&quot;&#x5C;&#x2F;topic&#x5C;&#x2F;1&quot;&#x5D;" '.
                 'data-symfony--ux-notify--notify-hub-value="'.$publicUrl.'"'.
             '></div>',
         ];
@@ -63,7 +63,7 @@ class NotifyRuntimeTest extends TestCase
             [],
             '<div '.
                 'data-controller="symfony--ux-notify--notify" '.
-                'data-symfony--ux-notify--notify-topics-value="[&quot;https:\/\/symfony.com\/notifier&quot;]" '.
+                'data-symfony--ux-notify--notify-topics-value="&#x5B;&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;symfony.com&#x5C;&#x2F;notifier&quot;&#x5D;" '.
                 'data-symfony--ux-notify--notify-hub-value="'.$publicUrl.'"'.
             '></div>',
         ];
diff --git a/src/React/tests/Twig/ReactComponentExtensionTest.php b/src/React/tests/Twig/ReactComponentExtensionTest.php
index 9fbf8c8b..aae2d6b6 100644
--- a/src/React/tests/Twig/ReactComponentExtensionTest.php
+++ b/src/React/tests/Twig/ReactComponentExtensionTest.php
@@ -36,7 +36,7 @@ class ReactComponentExtensionTest extends TestCase
         );
 
         $this->assertSame(
-            'data-controller="symfony--ux-react--react" data-symfony--ux-react--react-component-value="SubDir/MyComponent" data-symfony--ux-react--react-props-value="{&quot;fullName&quot;:&quot;Titouan Galopin&quot;}"',
+            'data-controller="symfony--ux-react--react" data-symfony--ux-react--react-component-value="SubDir&#x2F;MyComponent" data-symfony--ux-react--react-props-value="&#x7B;&quot;fullName&quot;&#x3A;&quot;Titouan&#x20;Galopin&quot;&#x7D;"',
             $rendered
         );
     }
@@ -52,7 +52,7 @@ class ReactComponentExtensionTest extends TestCase
         $rendered = $extension->renderReactComponent('SubDir/MyComponent');
 
         $this->assertSame(
-            'data-controller="symfony--ux-react--react" data-symfony--ux-react--react-component-value="SubDir/MyComponent"',
+            'data-controller="symfony--ux-react--react" data-symfony--ux-react--react-component-value="SubDir&#x2F;MyComponent"',
             $rendered
         );
     }
diff --git a/src/StimulusBundle/tests/Dto/StimulusAttributesTest.php b/src/StimulusBundle/tests/Dto/StimulusAttributesTest.php
index 17527a34..63393d99 100644
--- a/src/StimulusBundle/tests/Dto/StimulusAttributesTest.php
+++ b/src/StimulusBundle/tests/Dto/StimulusAttributesTest.php
@@ -145,7 +145,7 @@ final class StimulusAttributesTest extends TestCase
     public function testAddAttribute()
     {
         $this->stimulusAttributes->addAttribute('foo', 'bar baz');
-        $this->assertSame('foo="bar baz"', (string) $this->stimulusAttributes);
+        $this->assertSame('foo="bar&#x20;baz"', (string) $this->stimulusAttributes);
         $this->assertSame(['foo' => 'bar baz'], $this->stimulusAttributes->toArray());
     }
 }
diff --git a/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php b/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php
index 0e8cdbf8..41793ed5 100644
--- a/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php
+++ b/src/StimulusBundle/tests/Twig/StimulusTwigExtensionTest.php
@@ -135,8 +135,8 @@ final class StimulusTwigExtensionTest extends TestCase
         $extension = new StimulusTwigExtension(new StimulusHelper($this->twig));
         $dto = $extension->renderStimulusController('my-controller', ['myValue' => 'scalar-value']);
         $this->assertSame(
-            'data-controller="my-controller another-controller" data-my-controller-my-value-value="scalar-value" data-another-controller-another-value-value="scalar-value 2" data-another-controller-json-value-value="{&quot;key&quot;:&quot;Value with quotes &#039; and \&quot;.&quot;}"',
-            (string) $extension->appendStimulusController($dto, 'another-controller', ['another-value' => 'scalar-value 2', 'jsonValue' => json_encode(['key' => 'Value with quotes \' and ".'])]),
+            'data-controller="my-controller another-controller" data-my-controller-my-value-value="scalar-value" data-another-controller-another-value-value="scalar-value&#x20;2"',
+            (string) $extension->appendStimulusController($dto, 'another-controller', ['another-value' => 'scalar-value 2']),
         );
     }
 
diff --git a/src/Svelte/tests/Twig/SvelteComponentExtensionTest.php b/src/Svelte/tests/Twig/SvelteComponentExtensionTest.php
index 7a9a6d03..8231c028 100644
--- a/src/Svelte/tests/Twig/SvelteComponentExtensionTest.php
+++ b/src/Svelte/tests/Twig/SvelteComponentExtensionTest.php
@@ -37,7 +37,7 @@ class SvelteComponentExtensionTest extends TestCase
         );
 
         $this->assertSame(
-            'data-controller="symfony--ux-svelte--svelte" data-symfony--ux-svelte--svelte-component-value="SubDir/MyComponent" data-symfony--ux-svelte--svelte-props-value="{&quot;fullName&quot;:&quot;Titouan Galopin&quot;}"',
+            'data-controller="symfony--ux-svelte--svelte" data-symfony--ux-svelte--svelte-component-value="SubDir&#x2F;MyComponent" data-symfony--ux-svelte--svelte-props-value="&#x7B;&quot;fullName&quot;&#x3A;&quot;Titouan&#x20;Galopin&quot;&#x7D;"',
             $rendered
         );
     }
@@ -53,7 +53,7 @@ class SvelteComponentExtensionTest extends TestCase
         $rendered = $extension->renderSvelteComponent('SubDir/MyComponent');
 
         $this->assertSame(
-            'data-controller="symfony--ux-svelte--svelte" data-symfony--ux-svelte--svelte-component-value="SubDir/MyComponent"',
+            'data-controller="symfony--ux-svelte--svelte" data-symfony--ux-svelte--svelte-component-value="SubDir&#x2F;MyComponent"',
             $rendered
         );
     }
@@ -73,7 +73,7 @@ class SvelteComponentExtensionTest extends TestCase
         );
 
         $this->assertSame(
-            'data-controller="symfony--ux-svelte--svelte" data-symfony--ux-svelte--svelte-component-value="SubDir/MyComponent" data-symfony--ux-svelte--svelte-props-value="{&quot;fullName&quot;:&quot;Titouan Galopin&quot;}" data-symfony--ux-svelte--svelte-intro-value="true"',
+            'data-controller="symfony--ux-svelte--svelte" data-symfony--ux-svelte--svelte-component-value="SubDir&#x2F;MyComponent" data-symfony--ux-svelte--svelte-props-value="&#x7B;&quot;fullName&quot;&#x3A;&quot;Titouan&#x20;Galopin&quot;&#x7D;" data-symfony--ux-svelte--svelte-intro-value="true"',
             $rendered
         );
     }
diff --git a/src/Translator/assets/test/translator.test.ts b/src/Translator/assets/test/translator.test.ts
index 6df854a1..5a468044 100644
--- a/src/Translator/assets/test/translator.test.ts
+++ b/src/Translator/assets/test/translator.test.ts
@@ -4,7 +4,6 @@ import {
     type NoParametersType,
     setLocale,
     setLocaleFallbacks,
-    throwWhenNotFound,
     trans,
 } from '../src/translator';
 
@@ -12,7 +11,6 @@ describe('Translator', () => {
     beforeEach(() => {
         setLocale(null);
         setLocaleFallbacks({});
-        throwWhenNotFound(false);
         document.documentElement.lang = '';
         document.documentElement.removeAttribute('data-symfony-ux-translator-locale');
     });
@@ -389,40 +387,6 @@ describe('Translator', () => {
             );
         });
 
-        test('missing message should return the message id when `throwWhenNotFound` is false', () => {
-            throwWhenNotFound(false);
-            setLocale('fr');
-
-            const MESSAGE_IN_ANOTHER_DOMAIN: Message<{ security: { parameters: NoParametersType } }, 'en'> = {
-                id: 'Invalid credentials.',
-                translations: {
-                    messages: {
-                        en: 'Invalid credentials.',
-                    },
-                },
-            };
-
-            expect(trans(MESSAGE_IN_ANOTHER_DOMAIN)).toEqual('Invalid credentials.');
-        });
-
-        test('missing message should throw an error if `throwWhenNotFound` is true', () => {
-            throwWhenNotFound(true);
-            setLocale('fr');
-
-            const MESSAGE_IN_ANOTHER_DOMAIN: Message<{ security: { parameters: NoParametersType } }, 'en'> = {
-                id: 'Invalid credentials.',
-                translations: {
-                    messages: {
-                        en: 'Invalid credentials.',
-                    },
-                },
-            };
-
-            expect(() => {
-                trans(MESSAGE_IN_ANOTHER_DOMAIN);
-            }).toThrow(`No translation message found with id "Invalid credentials.".`);
-        });
-
         test('message from intl domain should be prioritized over its non-intl equivalent', () => {
             const MESSAGE: Message<
                 { 'messages+intl-icu': { parameters: NoParametersType }; messages: { parameters: NoParametersType } },
diff --git a/src/TwigComponent/tests/Unit/ComponentAttributesTest.php b/src/TwigComponent/tests/Unit/ComponentAttributesTest.php
index 8ed7aa4f..5e22ac73 100644
--- a/src/TwigComponent/tests/Unit/ComponentAttributesTest.php
+++ b/src/TwigComponent/tests/Unit/ComponentAttributesTest.php
@@ -147,7 +147,7 @@ final class ComponentAttributesTest extends TestCase
             'data-controller' => 'foo live',
             'data-live-data-value' => '{}',
             'data-foo-name-value' => 'ryan',
-            'data-foo-some-array-value' => '[&quot;a&quot;,&quot;b&quot;]',
+            'data-foo-some-array-value' => '&#x5B;&quot;a&quot;,&quot;b&quot;&#x5D;',
         ], $attributes->all());
     }
 
diff --git a/src/TwigComponent/tests/Unit/ComponentFactoryTest.php b/src/TwigComponent/tests/Unit/ComponentFactoryTest.php
new file mode 100644
index 00000000..fd1fd337
--- /dev/null
+++ b/src/TwigComponent/tests/Unit/ComponentFactoryTest.php
@@ -0,0 +1,92 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\UX\TwigComponent\Tests\Unit;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
+use Symfony\UX\TwigComponent\ComponentFactory;
+use Symfony\UX\TwigComponent\ComponentTemplateFinderInterface;
+
+/**
+ * @author Simon André <smn.andre@gmail.com>
+ */
+class ComponentFactoryTest extends TestCase
+{
+    public function testMetadataForConfig(): void
+    {
+        $factory = new ComponentFactory(
+            $this->createMock(ComponentTemplateFinderInterface::class),
+            $this->createMock(ServiceLocator::class),
+            $this->createMock(PropertyAccessorInterface::class),
+            $this->createMock(EventDispatcherInterface::class),
+            ['foo' => ['key' => 'foo', 'template' => 'bar.html.twig']],
+            []
+        );
+
+        $metadata = $factory->metadataFor('foo');
+
+        $this->assertSame('foo', $metadata->getName());
+        $this->assertSame('bar.html.twig', $metadata->getTemplate());
+    }
+
+    public function testMetadataForResolveAlias(): void
+    {
+        $factory = new ComponentFactory(
+            $this->createMock(ComponentTemplateFinderInterface::class),
+            $this->createMock(ServiceLocator::class),
+            $this->createMock(PropertyAccessorInterface::class),
+            $this->createMock(EventDispatcherInterface::class),
+            [
+                'bar' => ['key' => 'bar', 'template' => 'bar.html.twig'],
+                'foo' => ['key' => 'foo', 'template' => 'foo.html.twig'],
+            ],
+            ['Foo\\Bar' => 'bar'],
+        );
+
+        $metadata = $factory->metadataFor('Foo\\Bar');
+
+        $this->assertSame('bar', $metadata->getName());
+        $this->assertSame('bar.html.twig', $metadata->getTemplate());
+    }
+
+    public function testMetadataForReuseAnonymousConfig(): void
+    {
+        $templateFinder = $this->createMock(ComponentTemplateFinderInterface::class);
+        $templateFinder->expects($this->atLeastOnce())
+            ->method('findAnonymousComponentTemplate')
+            ->with('foo')
+            ->willReturnOnConsecutiveCalls('foo.html.twig', 'bar.html.twig', 'bar.html.twig');
+
+        $factory = new ComponentFactory(
+            $templateFinder,
+            $this->createMock(ServiceLocator::class),
+            $this->createMock(PropertyAccessorInterface::class),
+            $this->createMock(EventDispatcherInterface::class),
+            [],
+            []
+        );
+
+        $metadata = $factory->metadataFor('foo');
+        $this->assertSame('foo', $metadata->getName());
+        $this->assertSame('foo.html.twig', $metadata->getTemplate());
+
+        $metadata = $factory->metadataFor('foo');
+        $this->assertSame('foo', $metadata->getName());
+        $this->assertSame('foo.html.twig', $metadata->getTemplate());
+
+        $metadata = $factory->metadataFor('foo');
+        $this->assertSame('foo', $metadata->getName());
+        $this->assertSame('foo.html.twig', $metadata->getTemplate());
+    }
+}
diff --git a/src/Vue/tests/Twig/VueComponentExtensionTest.php b/src/Vue/tests/Twig/VueComponentExtensionTest.php
index 6a539395..3c618cca 100644
--- a/src/Vue/tests/Twig/VueComponentExtensionTest.php
+++ b/src/Vue/tests/Twig/VueComponentExtensionTest.php
@@ -37,7 +37,7 @@ class VueComponentExtensionTest extends TestCase
         );
 
         $this->assertSame(
-            'data-controller="symfony--ux-vue--vue" data-symfony--ux-vue--vue-component-value="SubDir/MyComponent" data-symfony--ux-vue--vue-props-value="{&quot;fullName&quot;:&quot;Titouan Galopin&quot;}"',
+            'data-controller="symfony--ux-vue--vue" data-symfony--ux-vue--vue-component-value="SubDir&#x2F;MyComponent" data-symfony--ux-vue--vue-props-value="&#x7B;&quot;fullName&quot;&#x3A;&quot;Titouan&#x20;Galopin&quot;&#x7D;"',
             $rendered
         );
     }
@@ -53,7 +53,7 @@ class VueComponentExtensionTest extends TestCase
         $rendered = $extension->renderVueComponent('SubDir/MyComponent');
 
         $this->assertSame(
-            'data-controller="symfony--ux-vue--vue" data-symfony--ux-vue--vue-component-value="SubDir/MyComponent"',
+            'data-controller="symfony--ux-vue--vue" data-symfony--ux-vue--vue-component-value="SubDir&#x2F;MyComponent"',
             $rendered
         );
     }