# Intégrer Leaflet dans un bloc : retour sur un bloc carte interactive

> Retour d'expérience sur l'intégration de Leaflet dans un bloc Gutenberg pour un réseau de points de vente : chargement conditionnel, configuration et pièges d'éditeur.

- Auteur : Clément Hadrot
- Publié le : 2023-12-29
- Mis à jour le : 2023-12-29
- Catégorie : Blocs Gutenberg
- URL : https://wpmoderne.dev.wordpress-developpement.fr/blocs/integrer-leaflet-bloc-carte-interactive-retour-experience/

## L’essentiel

- Chargement de Leaflet uniquement sur les pages qui en ont besoin
- L'éditeur et le front nécessitent deux initialisations distinctes de la carte
- Les données de points doivent être injectées sans dépendre du DOM WordPress

Un réseau de magasins de bricolage voulait une carte interactive de ses points de vente, insérable comme bloc sur n'importe quelle page, avec filtrage par région et affichage d'infobulles au clic. Le choix s'est porté sur Leaflet plutôt que sur l'API Google Maps, pour des raisons de coût de licence et parce que le projet utilisait déjà des tuiles OpenStreetMap sur une autre application interne du client.

Intégrer une bibliothèque JavaScript tierce comme Leaflet dans un bloc Gutenberg pose des questions qui ne se posent pas avec les composants natifs de l'éditeur : comment charger la bibliothèque sans alourdir toutes les pages du site, comment l'initialiser à la fois dans l'éditeur (pour l'aperçu) et sur le front, et comment lui transmettre des données dynamiques sans dépendre de l'état du DOM produit par WordPress.

## Charger Leaflet sans peser sur tout le site

Premier réflexe écarté : enregistrer Leaflet en dépendance globale via `wp_enqueue_script()` sur toutes les pages. Le bloc carte n'apparaissant que sur une dizaine de pages du site, charger une bibliothèque de plus de 40 Ko gzippée partout aurait été un gaspillage inutile. La solution retenue : enregistrer le script et sa feuille de style, mais ne les mettre en file d'attente que si le bloc est effectivement présent dans le contenu, via `has_block()`.

```
function agence_carte_enqueue_assets() {
    if ( ! has_block( 'agence/carte-points-vente' ) ) {
        return;
    }

    wp_enqueue_style(
        'leaflet',
        'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css',
        array(),
        '1.9.4'
    );
    wp_enqueue_script(
        'leaflet',
        'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
        array(),
        '1.9.4',
        true
    );
    wp_enqueue_script(
        'agence-carte-frontend',
        plugins_url( 'build/frontend.js', __FILE__ ),
        array( 'leaflet' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/frontend.js' ),
        true
    );
}
add_action( 'wp_enqueue_scripts', 'agence_carte_enqueue_assets' );
```

Pour la production, on a ensuite rapatrié les fichiers Leaflet en local dans le dépôt plutôt que de dépendre d'un CDN externe, pour des raisons de fiabilité et de conformité RGPD sur le chargement de ressources tierces.

## Deux initialisations, deux contextes

Un piège rencontré assez tôt : Leaflet manipule directement le DOM pour dessiner sa carte (méthode `L.map()` ciblant un élément par son `id`), ce qui entre en tension avec le cycle de rendu React de l'éditeur. Initialiser la carte dans la fonction `edit()` avec un simple `useEffect` fonctionnait, mais se cassait dès que l'utilisateur changeait de bloc sélectionné, car React re-rendait le conteneur sans que Leaflet ne le sache.

```
useEffect( () => {
    if ( ! conteneurRef.current || carteRef.current ) {
        return;
    }
    carteRef.current = L.map( conteneurRef.current ).setView( [ 46.6, 2.4 ], 6 );
    L.tileLayer( 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' ).addTo( carteRef.current );

    return () => {
        carteRef.current?.remove();
        carteRef.current = null;
    };
}, [] );
```

La clé a été de garder une référence stable à l'instance de carte (`carteRef`) et de nettoyer explicitement avec `.remove()` dans la fonction de nettoyage du `useEffect`, plutôt que de laisser Leaflet et React se disputer le même nœud DOM sans coordination.

> L'essentiel à retenir : Chargement de Leaflet uniquement sur les pages qui en ont besoin ; L'éditeur et le front nécessitent deux initialisations distinctes de la carte ; Les données de points doivent être injectées sans dépendre du DOM WordPress

## Transmettre les données de points sans dépendre du DOM WordPress

Les points de vente proviennent d'un custom post type, récupérés via une route REST personnalisée plutôt que via l'API REST des posts par défaut, pour ne renvoyer que les champs nécessaires (coordonnées, nom, horaires) et limiter le poids de la réponse.

```
{
    "points": [
        { "id": 12, "nom": "Agence Lyon Part-Dieu", "lat": 45.760, "lng": 4.860 },
        { "id": 13, "nom": "Agence Bordeaux Centre", "lat": 44.837, "lng": -0.579 }
    ]
}
```

Côté `view.js` du front, les données ne sont jamais lues depuis des attributs `data-*` fragiles injectés dans le HTML : elles sont chargées via `apiFetch` au montage du composant, avec un état de chargement affiché pendant la requête et un message de repli en cas d'échec réseau.

## Ce que ce projet n'a pas cherché à faire

Contrairement à un bloc construit avec l'Interactivity API, cette carte reposait sur une hydratation React classique côté front, car le projet a démarré avant la stabilisation de cette API. Un choix qui, avec le recul, alourdit le poids JavaScript envoyé aux visiteurs par rapport à une approche plus moderne — un sujet distinct qui mériterait sa propre migration, mais qui n'a pas été traité ici.

### Bilan des performances

- Le script Leaflet et son plugin de rendu personnalisé pèsent environ 55 Ko gzippés au total, chargés uniquement sur les pages concernées.
- Le rendu de 45 points simultanés avec infobulles reste fluide sans regroupement (clustering), mais l'équipe a prévu d'ajouter `Leaflet.markercluster` si le nombre de points de vente dépasse la centaine.
- Le chargement conditionnel via `has_block()` a évité d'alourdir les 200 autres pages du site qui n'affichent jamais cette carte.

> Le conseil qu'on retient de ce projet : quand une bibliothèque tierce manipule le DOM directement, ne jamais laisser React et cette bibliothèque écrire sur le même nœud sans qu'un des deux ne « possède » clairement le cycle de vie de cet élément.

## Pour aller plus loin

Ce retour d'expérience montre qu'intégrer une bibliothèque tierce dans un bloc demande de traiter séparément trois problèmes : le chargement conditionnel des scripts, la coexistence avec le cycle de rendu de l'éditeur, et l'acheminement des données dynamiques indépendamment du HTML statique généré par WordPress. Une checklist utile à toute équipe qui envisage d'intégrer une carte, un graphique ou un lecteur média tiers dans un bloc personnalisé.
