Expression fields
A field whose name ends in -expr takes a raw
MapLibre expression —
the same JSON array form MapLibre uses, not a rewrite of it.
"landuse": { "op": "fill-solid", "features": "@landuse_f", "fill-expr": ["match", ["get", "kind"], "park", "#a6c084", "forest", "#8fae74", "#e8e4d8"]}Evaluation is by maplibre-expr at
100 % conformance against MapLibre’s official spec fixtures. See
expression conformance.
The pairing convention
Section titled “The pairing convention”Where a paint value can be data-driven, the op offers both forms, and the
-expr field wins when both are present:
| Literal field | Expression field |
|---|---|
fill |
fill-expr |
color |
color-expr |
width-px |
width-expr |
radius-px |
radius-expr |
opacity |
opacity-expr |
The per-op tables in the node catalog are generated from the registry, so they are the authoritative list of which fields an op actually accepts.
An -expr does not stand in for the literal
Section titled “An -expr does not stand in for the literal”The expression wins at paint time, but it does not satisfy the field. A required literal stays required:
// ✅ `width-px` defaults to 1, so the expression alone is enough"roads": { "op": "stroke", "features": "@r", "color": "#000000", "width-expr": ["interpolate", ["linear"], ["zoom"], 0, 1, 10, 4] }
// ❌ missing required field `amplitude-px`"wob": { "op": "wave", "features": "@r", "wavelength-px": 20, "amplitude-px-expr": ["literal", 3] }The difference is not arbitrary. An expression is evaluated per feature while the tile is being painted, and some fields must be known before that: a field that decides canvas padding is one, since padding is fixed when the graph is built. Supply the literal as well — it is the value used when the expression is absent or fails for a feature, and for a padding-determining field it is also what the canvas is sized by. See padding and neighbours.
filter-expr on a features node is the same mechanism used as a predicate:
"water_f": { "op": "features", "layer": "water", "filter-expr": ["all", ["==", ["get", "kind"], "water"], ["has", "name"]] }Legacy-form filters — bare field names, !in, !has, none — are accepted and
converted with MapLibre’s own pre-compile conversion, strict-type semantics
included.
Evaluation context
Section titled “Evaluation context”| Where | Runs | Sees |
|---|---|---|
a paint op’s *-expr |
once per feature | that feature’s properties, and the tile’s zoom |
filter-expr |
once per feature | the same |
the expr op |
once per tile | the tile’s zoom (no feature) |
So a ["zoom"]-only curve is cheaper as an expr node — evaluated once, emitted
as a Scalar, cached like any other node:
"w": { "op": "expr", "expr": ["interpolate", ["linear"], ["zoom"], 10, 0.5, 16, 4] },"roads": { "op": "stroke", "features": "@roads_f", "width-px": "@w" }Zoom is the tile’s integer zoom. There is no fractional zoom, because there is no camera — a tile is rendered at its own zoom level. A MapLibre curve therefore evaluates at integer steps rather than continuously; this is the main visible consequence of tile-based rendering on zoom functions.
Expression, param, or scalar chain?
Section titled “Expression, param, or scalar chain?”All three produce a number, and they differ in when the value is fixed:
"sigma": ["interpolate", …] // ❌ not a thing — expressions go on `-expr` fields"sigma": 3 // fixed in the document"sigma": "$softness" // overridable per render by the caller"sigma": "@w" // computed by the graph (expr / math / zoom)Choose $param when a caller should be able to change it (CLI flag, query
string, slider). Choose an expression or a scalar chain when the value is a
property of the style. See
params and functions.
- An expression is part of the node’s cache key, so using one costs nothing in cacheability.
heatmap-style colour functions over density are baked into a 256-entry ramp per tile (ramp-exproncolor-ramp).- What can be type-checked statically is reported at build time; the rest is reported per feature at render time.
Map renders on this site are made fromOpenStreetMap data viaProtomaps (© OpenStreetMap contributors), elevation from Re:Earth Terrain,Mapterhorn andEGM2008 (NGA), and aerial imagery from GSI Japan(© 国土地理院). The painterly styles use CC0 brushes byDavid Revoy.