How to Style Map Layers in Mapbox GL JS
Tags: tutorial, javascript, react, mapbox
Published: 2021-03-03 15:31:58

> This post is part of my [**Building Interactive Maps with React**](https://gumroad.com/l/mapbox-handbook) course - a course for anyone wanting to learn how to build interactive maps and integrate them into their React applications. If you enjoy this guide, then chances are you will enjoy the course too!

The last few posts in this series have focused on [integrating spatial data into Mapbox Studio](https://www.lostcreekdesigns.co/writing/tilesets-and-datasets-managing-data-in-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](https://www.lostcreekdesigns.co/writing/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.

- [All of the layout and paint properties available by layer type](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#fill)
- [The difference between the layout and paint properties](https://docs.mapbox.com/help/glossary/layout-paint-property/)

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.

```jsx
map.addLayer({
  id: "rivers-layer",
  type: "line",
  source: "rivers",
	layout: {
		"line-cap": "round",
		"visibility": "visible"
	},
  paint: {
		"line-color": "#6382f2",
    "line-width": 2
  },
}); 
```

## 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](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#fill).

### 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.

```jsx
map.addLayer({
  id: "bus-stops-circle",
  type: "circle",
  source: "bus-stops",
  paint: {
    "circle-color": "#1d1485",
    "circle-radius": 8,
    "circle-stroke-color": "#ffffff",
    "circle-stroke-width": 2,
  },
});
```

### 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**

- [setPaintProperty()](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#setpaintproperty)
- [setLayoutProperty()](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#setlayoutproperty)
- [example of `setPaintProperty()` in action](https://docs.mapbox.com/mapbox-gl-js/example/color-switcher/)

```jsx
// add the layer to the map but have it be hidden initially
map.addLayer({
  id: "bus-stops-circle",
  type: "circle",
  source: "bus-stops",
  layout: {
		visibility: "none",
	},
  paint: {
    "circle-color": "#1d1485",
    "circle-radius": 8,
    "circle-stroke-color": "#ffffff",
    "circle-stroke-width": 2,
  },
});

// layer visibility toggle handler that could be attached
// elsewhere in your application
// something like toggleLayerVisibility('bus-stops-circle')
function toggleLayerVisibility(layerId) {
	const visibility = map.getLayoutProperty(layerId, 'visibility');
 
	if (visibility === 'visible') {
		map.setLayoutProperty(layerId, 'visibility', 'none');
	} else {
		map.setLayoutProperty(layerId, 'visibility', 'visible');
	}
}

// example of how you set invidual paint properties
function changeCircleColor(layerId, color) {
	map.setPaintProperty(layerId, 'circle-color', color);
}
```

### 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](https://docs.mapbox.com/mapbox-gl-js/style-spec/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**

- [Style circles with a data-driven property](https://docs.mapbox.com/mapbox-gl-js/example/data-driven-circle-colors/)
- [Mapbox GL JS Expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/)
- [Get started with Mapbox GL JS expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/)

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

```jsx
map.addLayer({
  id: "rivers-line",
  type: "line",
  source: "rivers",
  paint: {
		"line-color": "#6382f2",
    "line-width": 2,
		// make streams larger as the user zooms from z0 to z22
		"line-width": {
			"base": 2, // default value
			"stops": [
				// first # is the zoom level, second # is the style val
				[0, 8],
				[12, 4],
				[22, 1]
			]
		},
		// color lines based on water quality data
		"line-color": [
			"match",
      ["get", "quality"],
      "poor",
      "#f84c35",
			"average",
			"#f84c35",
			"good",
			"#f84c35",
			"#dddddd" // fallback value
		]
  },
});
```

## 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 this post useful, please retweet, share, or pick up a copy of [The Mapbox Developer's Handbook](https://gumroad.com/l/mapbox-handbook)!

## Useful Links and Resources

- [All of the layout and paint properties available by layer type](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#fill)
- [The difference between the layout and paint properties](https://docs.mapbox.com/help/glossary/layout-paint-property/)
- [setPaintProperty()](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#setpaintproperty)
- [setLayoutProperty()](https://docs.mapbox.com/mapbox-gl-js/api/map/#map#setlayoutproperty)
- [example of `setPaintProperty()` in action](https://docs.mapbox.com/mapbox-gl-js/example/color-switcher/)
- [Style circles with a data-driven property](https://docs.mapbox.com/mapbox-gl-js/example/data-driven-circle-colors/)
- [Mapbox GL JS Expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/)
- [Get started with Mapbox GL JS expressions](https://docs.mapbox.com/help/tutorials/mapbox-gl-js-expressions/)