Skip to content

Layers & styling

A layer draws one source’s data in one way: as circles, lines, filled areas, labels and so on. Each entry under layers: names its type, its source, and how it looks (paint: and layout:). Layers draw in order, the first at the bottom, all above the basemap.

Two layers can draw the same data. Here a fill shades each US state and a line outlines it, both from one source named under sources::

states.yaml
type: map
id: states
config:
center: [-98, 39]
zoom: 3
mapStyle: "https://tiles.openfreemap.org/styles/liberty"
sources:
states:
type: geojson
url: "https://maplibre.org/maplibre-gl-js/docs/assets/us_states.geojson"
layers:
- id: state-fill
type: fill
source: states
paint:
fill-color: "#22c55e"
fill-opacity: 0.4
- id: state-outline
type: line
source: states
paint:
line-color: "#166534"
line-width: 1
One source, two layers: a fill and its outline

Edit this map in the playground

The style keys (fill-color, line-width …) are MapLibre’s, unchanged: every one is in the MapLibre style spec. paint: holds how a layer is coloured and sized; layout: holds what is drawn at all (label text, line caps, visibility).

type:DrawsData
circleA dot per pointPoints
symbolLabels and iconsPoints, or along lines
lineLines and outlinesLines, polygons
fillFilled areasPolygons
fill-extrusionAreas raised in 3D, such as buildingsPolygons
heatmapPoint density as a colour surfacePoints
rasterImagery and scanned mapsRaster tiles, COG files
hillshadeShaded reliefElevation (raster-dem)
color-reliefElevation coloured by height (maplibre-gl 5.6 or later)Elevation (raster-dem)
backgroundOne colour or pattern over the whole mapNone

A background layer covers the basemap, because your layers draw above it; use it on a map without a basemap, or slide it under the basemap’s layers with before:.

These snippets are entries for a document’s layers: list. Each names a source (earthquakes, routes, states) that the document defines under sources: (Loading data).

- id: earthquakes
type: circle
source: earthquakes
paint:
circle-radius: 6
circle-color: "#ef4444"
circle-stroke-width: 1
circle-stroke-color: "#ffffff"
- id: routes
type: line
source: routes
paint:
line-color: "#2563eb"
line-width: 4
layout:
line-cap: round
line-join: round
- id: states
type: fill
source: states
paint:
fill-color: "#22c55e"
fill-opacity: 0.5

Write colours in quotes ("#22c55e"): in YAML an unquoted # starts a comment, so the colour would be lost.

A symbol layer with text-field writes text at each feature:

- id: city-names
type: symbol
source: cities
layout:
text-field: ["get", "name"]
text-size: 13
text-offset: [0, 1.2] # below the point, in ems
paint:
text-color: "#1f2937"
text-halo-color: "#ffffff"
text-halo-width: 1.5

Without text-font, a label uses the basemap’s regular font. A font has to come from the basemap’s glyph server, so if you name one, name a font that server has: OpenFreeMap’s styles serve "Noto Sans Regular", "Noto Sans Bold" and "Noto Sans Italic". A font the server lacks leaves the labels out and fills the console with failed downloads. The gallery has Change the case of labels, Display and style rich text labels, and Variable label placement.

fill-extrusion raises polygons by a height property. A layer can draw from the basemap’s own sources by name: OpenFreeMap’s styles call theirs openmaptiles, and its building layer has a height:

- id: buildings-3d
type: fill-extrusion
source: openmaptiles
source-layer: building
minzoom: 15
paint:
fill-extrusion-color: "#aab4c3"
fill-extrusion-height: ["get", "render_height"]
fill-extrusion-opacity: 0.8

Tilt the map (pitch: 50 in config:) to see the height. A heatmap turns many points into a density surface:

- id: quake-density
type: heatmap
source: earthquakes
paint:
heatmap-weight: ["interpolate", ["linear"], ["get", "mag"], 0, 0, 6, 1]
heatmap-radius: 20

See Display buildings in 3D and Create a heatmap layer for complete maps.

A raster layer shows image tiles, such as satellite imagery. hillshade and color-relief draw elevation data, from a raster-dem source: shading that shows the shape of the land, and colours by height. These go in a document beside its config::

sources:
imagery:
type: raster
tiles:
- "https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2020_3857/default/g/{z}/{y}/{x}.jpg"
tileSize: 256
attribution: "Sentinel-2 cloudless by EOX IT Services GmbH (contains modified Copernicus Sentinel data 2020)"
elevation:
type: raster-dem
url: "https://tiles.mapterhorn.com/tilejson.json"
encoding: terrarium
layers:
- id: imagery
type: raster
source: imagery
paint:
raster-opacity: 0.85
- id: relief
type: color-relief
source: elevation
paint:
color-relief-opacity: 0.5
color-relief-color:
- interpolate
- ["linear"]
- ["elevation"]
- 0
- "#2b83ba"
- 1500
- "#abdda4"
- 3000
- "#fdae61"
- 4500
- "#d7191c"
- id: hills
type: hillshade
source: elevation
paint:
hillshade-exaggeration: 0.6
  • Set tileSize: 256 for most raster tile servers. The default is 512, which stretches 256-pixel tiles and blurs them.
  • Set encoding on a raster-dem source. It defaults to mapbox, so terrarium tiles, such as Mapterhorn’s, need encoding: terrarium or the heights come out wrong.
  • color-relief needs maplibre-gl 5.6 or later. On an older version, the map skips the layer with one console warning and draws the rest. color-relief-color ramps over ["elevation"], in metres.

The same raster-dem source can also raise the map in 3D: Map documents › Terrain, sky & projection. Gallery: Display a satellite map, Add a hillshade layer, Add a color relief layer.

To colour or size each feature by one of its properties, write a MapLibre expression where a value would go. For classed maps (a choropleth, graduated circles, categories with a legend), classify: writes the expression and the legend for you: see Thematic maps & legends.

The examples here use the USGS earthquake feed, whose features carry a magnitude (mag) and a type.

quakes-by-magnitude.yaml
type: map
id: quakes-by-magnitude
config:
center: [-120, 37]
zoom: 4
mapStyle: "https://tiles.openfreemap.org/styles/liberty"
layers:
- id: earthquakes
type: circle
source:
type: geojson
url: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson"
paint:
circle-radius: ["interpolate", ["linear"], ["get", "mag"], 1, 3, 6, 20]
circle-color: ["interpolate", ["linear"], ["get", "mag"], 1, "#fef08a", 6, "#dc2626"]
circle-opacity: 0.8
Radius and colour grow with magnitude

Edit this map in the playground

["get", "mag"] reads the property; the pairs after it are input and output (magnitude 1 → radius 3, magnitude 6 → radius 20), with values in between blended.

paint:
circle-color:
- match
- ["get", "type"]
- "earthquake"
- "#ef4444"
- "quarry blast"
- "#3b82f6"
- "#6b7280" # every other value
paint:
circle-color:
- step
- ["get", "mag"]
- "#22c55e" # below 2.5
- 2.5
- "#eab308" # 2.5 up to 4.5
- 4.5
- "#ef4444" # 4.5 and above

Every expression is in the style spec’s expressions reference. The gallery’s Style lines with a data-driven property is a complete example; more are in the gallery’s Layers, icons & styling group.

filter: keeps only the features that match an expression:

- id: large-earthquakes
type: circle
source: earthquakes
filter: [">=", ["get", "mag"], 4.5]
paint:
circle-color: "#ef4444"

minzoom and maxzoom limit the zoom levels a layer draws at (minzoom: 12 hides it when zoomed out further). visible: false starts it hidden.

To let readers turn a layer on and off, give it a label:. The map then shows a layer panel in the top-right corner with a checkbox for each labelled layer; toggleable: false keeps a layer’s label without the checkbox.

Layers draw in the order you list them; later ones draw on top:

layers:
- id: areas # bottom
type: fill
- id: roads
type: line
- id: labels # top
type: symbol

All of them draw above the basemap. To slide a layer under part of the basemap, such as its place names, name that basemap layer in before: (before: place_label): your layer is drawn just below it. See Add a new layer below labels.

A layer with a legend: { label, color } entry, or a classify: block, is listed in the map’s legend. How the legend is built and placed is in Thematic maps › The legend.

Troubleshooting › The basemap draws but your layer doesn’t walks through an empty layer step by step. Problems specific to styling:

  • A colour is ignored: it is probably unquoted. Write "#ef4444".
  • Nothing draws at this zoom: check minzoom, maxzoom and filter:.
  • Labels don’t appear: the text-font you named isn’t on the basemap’s glyph server. Remove text-font, or name a font it serves.
  • A note about before:: before: names a layer the map doesn’t have. The layer is drawn on top instead, and the note suggests the closest layer id.

A style value that starts with =, such as text-field: "=upper({name})", is reserved and gets a warning: in mlym validate, in the browser console, and on the page while you develop. MapLibre receives the text exactly as written. Write an expression instead:

layout:
text-field: ["upcase", ["get", "name"]] # not "=upper({name})"

For text that really starts with =, wrap it in literal, which never warns: text-field: ["literal", "=5"]. MapLibre’s own == operator (["==", ["get", "kind"], "park"]) is not affected. The check covers a layer’s paint:, layout: and filter:, and a story chapter’s setPaintProperty and setLayoutProperty values.