MapLibre Style Spec — open-source vector map styling: fork history and current trajectory

2026-06-2511 min read
maplibrevector-tilesweb-mappingjsonstyle-specopen-source

MapLibre Style Specification is the open-source continuation of Mapbox Style v8, born in December 2020 after Mapbox switched its GL JS to a proprietary license. Legally it is a distinct specification; in practice it is backward-compatible with Mapbox Style v8 at the time of the fork plus several years of divergence. A style.json under MapLibre looks almost identical to one under Mapbox, but it doesn't tie you to a commercial company, doesn't require an API token, and doesn't track map loads. For open-source projects, government portals, and products with embedded maps, this is today's default choice.

Map generation of Berlin city center
Central Berlin's Mitte district, rendered via osm2cdr.ru. MapLibre emerged as the reaction of MapTiler and the OpenStreetMap community to Mapbox's licensing pivot — fittingly, one of the first public demos took place at the 2020 State of the Map in Berlin.

History: the December 2020 split

To understand MapLibre, you need to understand what happened to Mapbox in December 2020. Until that date Mapbox GL JS — the browser WebGL library for vector tile rendering — was distributed under the BSD-3-Clause license. This meant: any company could take its source, run it on its own server, pay no one, and depend on no Mapbox account. On that freedom a whole generation of cartographic startups grew: Stadia Maps, MapTiler, Carto, Klokan Technologies, Stamen Design — all built products around Mapbox GL JS and their own tile servers.

On December 10, 2020, Mapbox shipped Mapbox GL JS v2 with a license change to proprietary. Any use now required a Mapbox API token, was tracked on every "map load", and was billed past a free quota. Technically the old v1.13 under BSD remained, but all further development happened only in the proprietary branch. The community reacted painfully — in one day an ecosystem that had absorbed years of integration work was no longer open.

That same day, December 10, 2020, the GitHub repository maplibre/maplibre-gl-js appeared — a public fork of the latest BSD Mapbox GL JS v1.13. The initiators were engineers from Klokan Technologies (MapTiler), Stadia Maps, Microsoft, Stamen Design, Elastic, OpenStreetMap US — companies and individuals who needed an open-source continuation. Within weeks MapLibre formed as a non-profit organization, which in 2024 joined Linux Foundation as a hosted project.

From 2021 MapLibre Style Spec evolved independently. As of May 2026 the stable MapLibre GL JS version is v4. The spec remains fully compatible with Mapbox Style v8 "for reading" (legacy styles work unchanged) while having added its own features: globe projection projection: globe (v3, 2024), 3D terrain with DEM sources, an extended set of symbol properties. Mapbox in its proprietary branch moved on too — v3 added a model layer and Mapbox Standard, which MapLibre officially lacks. The result is two dialects of a common root, much like the relationship between LibreOffice and MS Office.

What's shared and where they diverged

The base structure of style.json is identical. version: 8, sources, sprite, glyphs, layers — all work the same. A style written for Mapbox v1.x (pre-December 2020) will most likely load into MapLibre with no changes. That's the whole point of MapLibre Style Spec as a "compatible continuation" — one-click migration.

Data sources (sources) match completely: vector, raster, raster-dem, geojson, image, video exist in both. Layer types background, fill, line, symbol, raster, circle, fill-extrusion, heatmap, hillshade are common. Mapbox v8 expressions (interpolate, step, match, case, get, has, all, any) ported to MapLibre unchanged.

Divergence begins with features added after the fork. MapLibre's unique additions: projection: globe — render the map as a 3D sphere, visually striking for travel and education apps. Supported since MapLibre GL JS v3. terrain with a DEM source adds relief via raster-dem and a shader effect — MapLibre Native supports it natively, MapLibre GL JS via WebGL. Symbol layer properties got richer: symbol-z-elevate, symbol-z-offset for z-ordering in 3D scenes.

Mapbox went its own way too. Its v3 (2023) added a model layer — rendering 3D building models in glTF format directly in the map. Slot-based composition (slots within styles) for thematic overlays. A fog layer for atmospheric haze on the horizon. Mapbox Standard — a new default style with a lighting system. None of this exists in MapLibre, and a style.json coming out of Mapbox Studio v3 needs to be "degraded" — strip out model-typed layer blocks, drop fog, slot, rewrite to classical approaches.

Custom layers and the custom layer type are a shared feature, predating the fork. Through it a developer can author WebGL shaders inside the map render. Works in both implementations, though the API has drifted slightly (MapLibre added more hooks).

What's inside a MapLibre file

The file structure repeats Mapbox v8 — use the Mapbox Style JSON article as reference. Here we focus on the keys MapLibre interprets or extends differently.

projection under MapLibre accepts mercator (Web Mercator, default), globe (since v3, sphere display), and vertical-perspective. The Mapbox-specific variants (albers, equalEarth, naturalEarth) are not mirrored in MapLibre — stick to the three listed.

terrain declares an elevation source by referencing a raster-dem source plus an exaggeration parameter. Works only on the desktop MapLibre GL JS build — mobile needs MapLibre Native.

sky is an atmospheric layer above the horizon, shown when the camera is tilted. Available since MapLibre v2.

hillshade-type layers render relief shadows from a DEM source. Paint parameters: hillshade-exaggeration, hillshade-shadow-color, hillshade-highlight-color. Handy for topographic base maps.

Expressions are fully Mapbox v8 compatible plus a few additions in MapLibre v4 — but the core set is the same.

sprite and glyphs behave identically. MapLibre styles often reference free glyph services from OpenMapTiles, Klokantech, or Demotiles MapLibre — all available without a token.

Who needs MapLibre Style Spec: five roles

Open-source developer. If your project is GPL/AGPL/Apache and avoiding commercial SDK lock-in with mandatory telemetry matters — MapLibre is essentially the only path. No tokens, no billing, no trackers. License — BSD-3-Clause in perpetuity.

Government service. State agencies, regions, municipal portals are often bound by data-sovereignty regulations and cannot rely on third-party APIs. Hosting a MapLibre style on your own server with your own PMTiles eliminates external dependencies entirely. This is the path taken by, for example, OpenFreeMap — global base tiles free for everyone under MapLibre.

Editor-tool builder. Maputnik, Felt, MapTiler Cloud — all build their visual editors on top of MapLibre Style Spec precisely because the spec is open, and any edits made in the editor can flow straight to any MapLibre renderer. When you are building your own cartographic tool, savings on licensing negotiations are enormous.

Wikipedia and Wikimedia. Since 2024 the Wikimedia family uses MapLibre GL JS for maps in articles and Wikidata — by virtue of openness and no-fee requirements. OpenStreetMap.org migrated its vector preview to MapLibre too. If your work integrates with these ecosystems, knowing MapLibre Style Spec is mandatory.

Embed provider with distributed clients. A service that embeds on third-party sites cannot afford to ship an API token on every view. MapLibre + PMTiles + static hosting is the standard for self-contained cartographic embeds, where the server doesn't participate in rendering at all.

Tools for editing and hosting: seven

MapLibre GL JS. Browser-side WebGL library, core of the ecosystem. npm install maplibre-gl. Constructor new maplibregl.Map({ style: '/style.json' }). BSD-3-Clause. maplibre.org/maplibre-gl-js

MapLibre Native. Cross-platform engine for iOS, Android, Linux, macOS, Windows. Used in mobile apps where a WebView is undesirable. C++ core plus bindings. maplibre.org/maplibre-native

Maputnik. Visual style editor, open-source, in-browser. Imports and exports style.json, supports all layer types and expressions. Supports both Mapbox v8 and MapLibre extensions. maputnik.github.io

OpenFreeMap. Free global MapLibre tiles and styles, a project by Zsolt Ero. A full replacement for Mapbox/Google Maps base layers, no signup, no limits. Styles are published as JSON and easy to fork. openfreemap.org

MapTiler Cloud. Commercial tile and style hosting from Klokan Technologies (the original MapLibre committers). The MapTiler Customize designer enables visual style editing. Free tier of 100K loads per month. maptiler.com

TileServer GL. Node.js server, a Mapbox-version fork under MapLibre. Hosts MVT, renders raster tiles from style.json, serves TileJSON. Spins up in minutes via Docker. github.com/maptiler/tileserver-gl

Felt. Commercial collaborative map editor, uses MapLibre under the hood. Useful when you need team work on a single map with real-time edits. felt.com

Pitfalls and tips

Mapbox v2/v3 → MapLibre migration is not automatic. Styles from Mapbox Studio v3 contain model layers, fog, slot, projection: albers — MapLibre doesn't understand them. Open the file, remove the corresponding layer blocks, replace with classical fill/symbol. Automation utilities are still nascent (there are gists with scripts but no proper CLI).

Not all MapLibre versions are equal. MapLibre GL JS v1 handled Mapbox v8 one-to-one. v2 added terrain, v3 added globe, v4 added expanded expressions. If your style uses projection: globe, it won't load in MapLibre v2. Verify the renderer version on the client.

PMTiles is the standard self-hosting path. Unlike the Mapbox ecosystem, where referencing a TileJSON endpoint is customary, most MapLibre projects use PMTiles — a single-file archive on static hosting. Connection via MapLibre addProtocol('pmtiles', protocol.tile) is the pattern you'll see in most examples.

Fonts are the developer's responsibility. In a Mapbox account glyphs come out of the box. With MapLibre you either reference free glyph services (fonts.openmaptiles.org, tiles.demotiles.maplibre.org) or host your own pbfs. A ready-made set of Open Sans, Klokantech Noto Sans, Roboto is available in the OpenMapTiles fonts repository.

Custom layers require WebGL2. MapLibre v3+ needs WebGL2 for globe projection and custom shaders. Old Android up to Chrome 109 doesn't support it — about 2–3% of global traffic drops off. Test on older devices if your audience is broad.

Sprite sheets must be high-res. On retina displays MapLibre uses sprite@2x.png if present. If you only have sprite.png, icons will look blurry. Prepare both variants when building sprites via spritezero or a Squoosh pipeline.

Compatibility with Mapbox styles is asymmetric. A MapLibre v3 style.json with globe won't load in Mapbox GL JS (different projection syntax). A Mapbox v1.13 style.json loads in both. A Mapbox v3 style.json loads in Mapbox v3 but not in MapLibre. Plan compatibility direction in advance.

Performance is on par with Mapbox. Both implementations use WebGL and analogous pipelines. Benchmarks show ±5% differences. The choice between them is about licensing and features, not speed.

How to assemble a MapLibre style for your data via osm2cdr.ru

The approach is nearly identical to Mapbox, but with no API token registration and using free glyph services.

Step 1. On osm2cdr.ru outline the area of interest, choose PMTiles in the dropdown. Download the file — a single-file archive of all vector tiles for the chosen OSM fragment.

Step 2. Upload .pmtiles to static hosting with HTTP Range request support. Cloudflare R2, AWS S3 (with proper CORS), GitHub Pages with custom domain — all work. nginx with default config and Range support is fine too.

Step 3. Author style.json:

{
  "version": 8,
  "name": "OSM2CDR MapLibre",
  "sources": {
    "osm": {
      "type": "vector",
      "url": "pmtiles://https://cdn.example.com/region.pmtiles"
    }
  },
  "glyphs": "https://fonts.openmaptiles.org/{fontstack}/{range}.pbf",
  "sprite": "https://cdn.example.com/sprite",
  "layers": [
    {"id": "bg", "type": "background",
     "paint": {"background-color": "#f5f1eb"}},
    {"id": "water", "type": "fill", "source": "osm",
     "source-layer": "water",
     "paint": {"fill-color": "#a0c8e8"}},
    {"id": "landuse-park", "type": "fill", "source": "osm",
     "source-layer": "landuse",
     "filter": ["==", ["get", "class"], "park"],
     "paint": {"fill-color": "#d4e6c0"}},
    {"id": "roads", "type": "line", "source": "osm",
     "source-layer": "roads",
     "paint": {
       "line-color": "#666",
       "line-width": ["interpolate", ["exponential", 1.5], ["zoom"],
                      8, 0.5, 14, 2, 18, 8]
     }},
    {"id": "buildings", "type": "fill", "source": "osm",
     "source-layer": "buildings",
     "minzoom": 13,
     "paint": {"fill-color": "#ddd0c0", "fill-outline-color": "#bba"}},
    {"id": "place-labels", "type": "symbol", "source": "osm",
     "source-layer": "place",
     "layout": {
       "text-field": ["get", "name"],
       "text-font": ["Noto Sans Regular"],
       "text-size": 12
     },
     "paint": {"text-color": "#333", "text-halo-color": "#fff",
               "text-halo-width": 1.5}}
  ]
}

Step 4. Wire it in the browser:

<link href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css" rel="stylesheet">
<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>
<script src="https://unpkg.com/pmtiles/dist/pmtiles.js"></script>
<div id="map" style="height:100vh"></div>
<script>
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol('pmtiles', protocol.tile);
const map = new maplibregl.Map({
  container: 'map',
  style: '/style.json',
  center: [13.40, 52.52],
  zoom: 13,
  projection: 'globe'
});
</script>

Step 5. Open style.json in Maputnik, play with colors and filters, export back. No tokens, no billing, no external dependencies beyond a static CDN.

Sources

← All articles