I like to write almost as much as I like to build. I write about dashboards, maps, freelancing, music, and the outdoors. If this interests you too, please join my mailing list to get updates whenever I publish new content.

Subscribe

How to Style Map Layers in Mapbox GL JS

Date: 3/3/2021

Time to Read: 4 minutes

This post is part of my Building Interactive Maps with React course - a course for anyone wanting to learn how to build interactive maps and integrate them into their React applications. You can pre-order the course for $15 before the price goes up in early May.

The last few posts in this series have focused on integrating spatial data into Mapbox Studio and Mapbox GL JS applications. The logical next step is to focus on the presentation of spatial data.

The aim of this guide is to provide an overview and list of resources detailing how map layers can be styled using Mapbox GL JS. This guide will more closely resemble a collection of resources than a technical guide. If I opted to cover every way a map layer can be styled, I would be writing this post for the rest of the year.

Getting Started

This post picks up where A Complete Guide to Sources and Layers in React and Mapbox GL JS leaves off. To get the most out of this guide, you should be familiar with how to add sources and layers to a map using Mapbox GL JS. There are a couple of different ways to style map layers, but all rely on the same underlying style specification of layout and paint properties. We will begin here.

Layout and Paint Properties

The specification for a Mapbox layer has two style-related properties, layout and paint, that work in tandem to control how a map layer is rendered and styled. It can be a bit tricky to remember the difference between the two, but it is a safe bet to say (unless you are working with the symbol layer type) that you will be focused on the paint property the majority of the time. With the exception of the symbol and line layer, all of the layer types only have one valid layout property which is visibility.

Mapbox provides great documentation on the layout and paint properties.

Here is an example snippet showing both the layout and paint properties in action. This results in a visible layer with rounded line ends with a blue stroke and 2px stroke width.

1
map.addLayer({
2
id: "rivers-layer",
3
type: "line",
4
source: "rivers",
5
layout: {
6
"line-cap": "round",
7
visibility: "visible",
8
},
9
paint: {
10
"line-color": "#6382f2",
11
"line-width": 2,
12
},
13
})
14

Different Ways To Style a Layer

A powerful feature of Mapbox GL JS is that you can style map layers when they are added to the map or after. This provides a lot of flexibility in terms of allowing your map styles to adapt to changes in your application. You can find a full list of all the different styling options that available to each layer type here.

Styling a layer when it is added to the map

Using this approach, we apply the map styles when it is added to the map. Unless the map layer styling needs to respond to changes in your application or user input, this is the recommended approach. As you can see, most the time there isn't even a need to include the layout property when styling a layer.

1
map.addLayer({
2
id: "bus-stops-circle",
3
type: "circle",
4
source: "bus-stops",
5
paint: {
6
"circle-color": "#1d1485",
7
"circle-radius": 8,
8
"circle-stroke-color": "#ffffff",
9
"circle-stroke-width": 2,
10
},
11
})
12

Styling a layer after it is added to the map

There are some instances where you would want to delay styling the map layer or to apply new styling based on some change in your application. Some valid use cases include toggling layer visibility, changing the color of a layer based on user input, styling a layer based on data, etc. Luckily, the setPaintProperty() and setLayoutProperty() methods in Mapbox GL JS make this relatively painless.

More information

1
// add the layer to the map but have it be hidden initially
2
map.addLayer({
3
id: "bus-stops-circle",
4
type: "circle",
5
source: "bus-stops",
6
layout: {
7
visibility: "none",
8
},
9
paint: {
10
"circle-color": "#1d1485",
11
"circle-radius": 8,
12
"circle-stroke-color": "#ffffff",
13
"circle-stroke-width": 2,
14
},
15
})
16
17
// layer visibility toggle handler that could be attached
18
// elsewhere in your application
19
// something like toggleLayerVisibility('bus-stops-circle')
20
function toggleLayerVisibility(layerId) {
21
const visibility = map.getLayoutProperty(layerId, "visibility")
22
23
if (visibility === "visible") {
24
map.setLayoutProperty(layerId, "visibility", "none")
25
} else {
26
map.setLayoutProperty(layerId, "visibility", "visible")
27
}
28
}
29
30
// example of how you set invidual paint properties
31
function changeCircleColor(layerId, color) {
32
map.setPaintProperty(layerId, "circle-color", color)
33
}
34

Styling a Layer Conditionally

One of the most powerful aspects of styling is the ability to apply styles based on different conditions. Mapbox GL JS allows you to style layers based on

  • characteristics of the layer data (i.e. color all counties with a population of greater than some number blue)
  • the zoom range (i.e. when really zoomed in make the rivers map layer thinner but when really zoomed out make it thicker)

Implementing either approach relies on a core concept in Mapbox GL JS which is expressions. I honestly have to return to the docs every time I work with them. They are mighty powerful but also mighty confusing.

Tip! You can use Mapbox Studio to quickly prototype layer styling. This is especially valuable for data driven styling. Select the layer and paint property you would like to style (i.e. fill) and then select to style it across a zoom range, data range, or across data conditions in the UI. After you have things looking like you want, select the </> icon in the bottom right corner of the drawer. This will expose the Mapbox expression needed for the data/zoom driven styling. You can then easily copy and paste it into your code.

More information

Here is an example of using expressions for data-driven and zoom-driven styling.

1
map.addLayer({
2
id: "rivers-line",
3
type: "line",
4
source: "rivers",
5
paint: {
6
"line-color": "#6382f2",
7
"line-width": 2,
8
// make streams larger as the user zooms from z0 to z22
9
"line-width": {
10
base: 2, // default value
11
stops: [
12
// first # is the zoom level, second # is the style val
13
[0, 8],
14
[12, 4],
15
[22, 1],
16
],
17
},
18
// color lines based on water quality data
19
"line-color": [
20
"match",
21
["get", "quality"],
22
"poor",
23
"#f84c35",
24
"average",
25
"#f84c35",
26
"good",
27
"#f84c35",
28
"#dddddd", // fallback value
29
],
30
},
31
})
32

Next Steps

I encourage you to go deep on the styling topic. The art of styling a map is equally as important as bringing it to life with interactivity and should not be overlooked. My recommendation is to spend some exploring styling using the Mapbox Studio UI. It is a great place to come up to speed on all of the different ways you can style different layer types. It is is easy to iterate and then take what you learn and apply it in the context of Mapbox GL JS.

If you found thus post useful, give me a follow on Twitter or consider picking up a copy of the Building Interactive Maps with React course.

Useful Links and Resources

Join the Newsletter

I periodically send out a newsletter. Interested? Sign up below. You can unsubscribe at any time.

Interested in working together?

Drop me a line
© 2021 Lost Creek Designs