Leaflet.markercluster: Does not work with ES6 imports for leaflet

It seems this plugin makes the assumption that L is still exported globally. However using leaflet the ES6 way with direct imports like import { map } from "leaflet/src/map/Map"

does not expose a L global anymore. So trying to include this plugins MarkerClusterGroup fails cause of this definition.

export var MarkerClusterGroup = L.MarkerClusterGroup = L.FeatureGroup.extend({

The correct way would be to write this like this:

import { FeatureGroup } from "leaflet/src/layer/FeatureGroup"
export var MarkerClusterGroup = FeatureGroup.extend({

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 9
  • Comments: 30 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Coming back here after 2 years I’d have thought this issue to be resolved. It seems its still not possible to properly import leaflet and this plugin. I am going to open a new PR over at leaflet to suggest a 2.0 release and intentionally breaking all the plugins depending on a global ‘L’ var. It seems otherwise we can not get any plugin owner to move forward to packages.

Finally found the solution

instead of using import * as L from ‘leaflet’

I use

import L from ‘leaflet’

some reading http://www.thedreaming.org/2017/04/28/es6-imports-babel/

just in case anybody runs into the same situation I had here: I tried to go from;

import "leaflet";
import "leaflet.markercluster";

...
let mcluster = L.markerClusterGroup({ chunkedLoading: true });

to:

import { Map, Marker,  } from "leaflet";
import { MarkerClusterGroup } from "leaflet.markercluster/src";

...
let mcluster = MarkerClusterGroup({ chunkedLoading: true });

I banged my head against a wall until I realized I had to call it with new:

let mcluster = new MarkerClusterGroup({ chunkedLoading: true });

I guess this still has not been fixed?

I’ve made PR #984 that should fix this few days ago, I’d like some backup.

Basically in source files L is used as global and it leaked to the bundle. I’ve configured rollup to feed L variable from UMD require - which works as well with global L outside the bundle.

And it’s ok with guidelines: https://github.com/Leaflet/Leaflet/blob/master/PLUGIN-GUIDE.md#module-loaders

works fine for me if you just import all of it…

import 'leaflet/dist/leaflet.css' import 'leaflet/dist/leaflet' import 'leaflet-draw' import 'leaflet-draw/dist/leaflet.draw.css' import 'leaflet.markercluster' import 'leaflet.markercluster/dist/MarkerCluster.css'

@danzel What do you say about this? Shall we try to make this package independent on global L and instead use ES6 imports?

I’m using

"leaflet": "^1.3.1",
"leaflet.markercluster": "^1.3.0"

and

"@types/leaflet": "^1.2.6",
"@types/leaflet-markercluster": "^1.0.3"

The following worked for me:

import 'leaflet';
import 'leaflet.markercluster';

const L = window['L'];

Right under the imports, I have used this to declare L.

const L = window[‘L’];

Make things working on leaflet-src.esm.js and without window.L:

  • Leaflet.markercluster-1.3.0
  • leaflet-1.3.2 (NOT 1.3.3 or 1.3.1), see leaflet#6239
  • Need some shims using import/export loaders:
import Leaflet from 'leaflet'
import MarkerClusterGroup from 'imports-loader?L=leaflet!exports-loader?L.MarkerClusterGroup!./node_modules/leaflet.markercluster/dist/leaflet.markercluster.js'

// Create cluster:
val mcg = new MarkerClusterGroup(...);
Leaflet.addLayer(mcg);

Solution based on earlier crunch (L<=1.3.1).

Yes but then you throw away all the benefits of ES6 modules. Which is to load only what you really need.

A guess the way to solve this is to stop depending on window.L in all the files, and instead do ES import, like leaflet/leaflet does. It would be a pretty great step forward.

Same as above mentioned by @azrin1972 . Only difference in my setup is I’m using Webpack.

The 'L' is not defined no-undef is coming from eslint. The TypeError: L.markerClusterGroup is not a function is a fatal error that stops MarkerCluster from working.

Yeah, I’m interested in this proper solution as well…

I’ve taken from the above and was able to get it working, although, as mentioned without the benefit of ES6 and selectively loading what was needed.

// Import CSS from Leaflet and plugins.
import 'leaflet/dist/leaflet.css';
import 'leaflet.markercluster/dist/MarkerCluster.css';
import 'leaflet.markercluster/dist/MarkerCluster.Default.css';

// Import images directly that got missed via the CSS imports above.
import 'leaflet/dist/images/marker-icon-2x.png';
import 'leaflet/dist/images/marker-shadow.png';

// Import JS from Leaflet and plugins.
import 'leaflet/dist/leaflet';
import 'leaflet.markercluster/dist/leaflet.markercluster';
import 'leaflet.gridlayer.googlemutant/Leaflet.GoogleMutant';

Related PRs and discussions for Leaflet:

I’d love to figure out how to use the correct, minimalistic ES6 way with my custom Leaflet setup. The only plugins I’m using are markercluster, because who wouldn’t use it?! and the Google maps layer.

If I use: import * as L from 'leaflet'; instead of import 'leaflet/dist/leaflet';, I get TypeError: L.markerClusterGroup is not a function.

I was having a similar problem using leaflet in Vite: No matching export in "node_modules/leaflet/dist/leaflet-src.esm.js" for import "toLatLngBounds"

Using import 'leaflet/dist/leaflet'; instead import L from 'leaflet'; or import 'leaflet'; worked for me.

since we’re posting hotfixs. I’ve got a Vue app in Quasar 2. Before the Vue app is initialized:

import * as L from "leaflet/dist/leaflet-src.esm.js"
globalThis.L = L

in a component then:

  async mounted() {
    const { MarkerClusterGroup } = await import('leaflet.markercluster')
    console.log(this.$options.name, this.projects, MarkerClusterGroup)
  }