I'm trying to add a layer of place labels above a GeoJSON layer, so that these labels remain visible. However when I add the layer per the code below, the tileLayer remains below the GeoJSON layer.

    var CartoDB_PositronOnlyLabels = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="http://cartodb.com/attributions">CartoDB</a>',
        subdomains: 'abcd',
        maxZoom: 19,
        zIndex: 3

Adding a few more things that don't work for the future reader:

  1. According to this leaflet PR you could add a pane: 'overlayPane' option upon layer creation.
  2. The hack appearing here and here only works if the tileLayer you're adding is of tileJSON format

    var topPane = map._createPane('leaflet-top-pane', map.getPanes().mapPane);  
    var topLayer = L.mapbox.tileLayer('lxbarth.map-vtt23b1i').addTo(map);  
  • 1
    gis.stackexchange.com/questions/20331/… See this question. You have to alter the z-index.
    – Stella
    Jan 28, 2016 at 21:14
  • Nice try, but z-index only alters draw order within a pane, it won't make the tilelayer pane render above the overlay pane
    – raphael
    Jan 28, 2016 at 21:41

2 Answers 2


Panes might help your cause. The idea being you can control what layers draw in what panes and then control the pane order. Here is the original GIT discusson on it.

There is also a similar SO question regarding overlay layers and tile layers and how they are implemented on custom panes.


Barrett's answer set me down the right path, but I ended up in such a number of rabbit holes that I decided that posting my resulting code and explanation of some of my discoveries in the hopes this might help the community.

The resulting code that worked is as below.

var CartoDB_PositronOnlyLabels = {
    "tilejson": "2.0.1",
    "attribution": '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; 
         <a href="http://cartodb.com/attributions">CartoDB</a>',
    "maxzoom": 19,
    "minzoom": 13       

var topPane = map._createPane('leaflet-top-pane', map.getPanes().mapPane);
var topLayer = L.mapbox.tileLayer(CartoDB_PositronOnlyLabels).addTo(map);

The second half of that code is based on this answer. I had to modify it because the L.mapbox.tileLayer doesn't work the same way as L.tileLayer, it requires a tileJSON as input (spec), which is what that first section of code defines.

You'll notice a difference from the parameters provided in tileLayer code examples like the handy Leaflet Providers: the absence of the subdomain {s} in the url. This is because, well, this. Fortunately, cartoDB explicitly lists those subdomains (abcd) so I picked c and added that to the start of the tile URLs and it worked!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.