Migrate from Mapbox GL JS to MapLibre
MapLibre GL JS began in December 2020 as a fork of Mapbox GL JS 1.13, the last open-source version, after Mapbox moved version 2 to a proprietary license. From version 2 on, Mapbox GL JS needs a Mapbox access token, and each map it creates is a billable map load, whatever tiles it shows. MapLibre is open source under the BSD license, needs no token, and draws any vector tiles that follow the same style format.
Most of the API is the same, so the code changes are small. This guide moves a typical map over, with Mapricot's free tiles as the replacement for Mapbox's. It was tested with MapLibre GL JS 6.15 and Vite 8.
Before you start
This guide assumes an app that installs Mapbox GL JS with npm and builds with a bundler, such as Vite. You need:
- Node.js 20.19 or later, with npm, which the app already uses. Run
node --versionto check. - For the search box in step 5, a browser key with the Places product. Getting started shows how to make one. The map tiles need no key.
If your page loads Mapbox GL JS with a <script> tag instead, there's no package to swap. Replace Mapbox's script and stylesheet with MapLibre's from jsDelivr, as in the page Map tiles starts with, then follow steps 3 to 5. MapLibre GL JS 6 is a JavaScript module, so the map code moves into a <script type="module">, and mapboxgl. becomes maplibregl..
1. Swap the package
npm uninstall mapbox-gl
npm install maplibre-gl
2. Change the imports and drop the token
Before, with Mapbox:
import mapboxgl from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";
mapboxgl.accessToken = "pk.…";
const map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v12",
center: [-122.6793, 45.5189],
zoom: 15,
});
After, with MapLibre:
import * as maplibregl from "maplibre-gl";
import workerUrl from "maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url";
import "maplibre-gl/dist/maplibre-gl.css";
maplibregl.setWorkerUrl(workerUrl);
const map = new maplibregl.Map({
container: "map",
style: "https://tiles.mapricot.com/styles/light.json",
center: [-122.6793, 45.5189],
zoom: 15,
});
- No token. Delete
mapboxgl.accessToken, and anyaccessTokenoption. - The import. MapLibre GL JS 6 has no default export, so import it as a namespace, as above, or import the classes you use, such as
import { Map, Marker } from "maplibre-gl". - The worker. MapLibre GL JS 6 loads its web worker from a separate file. Under Vite, the map stays blank with
Worker failed to loadunless you set the worker's URL, which is what thesetWorkerUrllines do. Other bundlers have their own way to get the file's URL. - Everything else keeps its name:
Map,Marker,Popup,NavigationControl,addSource,addLayer,on("load"),flyTo, and the rest work as before.
3. Replace mapbox:// URLs
MapLibre can't load mapbox:// URLs, and Mapbox's styles and tiles need a Mapbox token anyway. Replace each one:
- The style. Use any MapLibre style URL. Mapricot's map tiles come in
light,dark, andgrayscalestyles athttps://tiles.mapricot.com/styles/{name}.json. They're free and need no key. - Sources you add yourself. A source like
url: "mapbox://mapbox.mapbox-terrain-v2"needs a replacement from another provider, or your own tiles. - Your own data in GeoJSON sources, and the layers that draw it, need no changes.
4. Rename CSS classes
The map's elements use a maplibregl- prefix instead of mapboxgl-. If your CSS styles them, rename the selectors, for example .mapboxgl-popup-content to .maplibregl-popup-content and .mapboxgl-ctrl-group to .maplibregl-ctrl-group.
5. Replace the geocoder
The Mapbox geocoder control calls Mapbox's geocoding API. @maplibre/maplibre-gl-geocoder is the same control without a built-in service: you give it a function that searches. This one uses Mapricot's search:
npm install @maplibre/maplibre-gl-geocoder
import MaplibreGeocoder from "@maplibre/maplibre-gl-geocoder";
import "@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css";
const geocoder = new MaplibreGeocoder(
{
forwardGeocode: async ({ query }) => {
const response = await fetch(`https://api.mapricot.com/v1/search?q=${encodeURIComponent(query)}`, {
headers: { Authorization: `Bearer ${import.meta.env.VITE_MAPRICOT_KEY}` },
});
const { results } = await response.json();
return {
features: results.map((result) => ({
type: "Feature",
geometry: { type: "Point", coordinates: [result.lon, result.lat] },
center: [result.lon, result.lat],
place_name: result.label,
properties: {},
})),
};
},
},
{ maplibregl },
);
map.addControl(geocoder);
The control expects results shaped like Mapbox's geocoding responses, which is why each result becomes a GeoJSON feature with center and place_name. The key is readable by anyone who opens the page, so use a browser key, which works only from the origins you allow, with only the places scope. Security explains how keys are protected.
What doesn't carry over
Mapbox has added features to Mapbox GL JS since the fork that MapLibre doesn't have, and MapLibre has added its own. The ones most likely to matter when you move:
- Mapbox's styles and data, including the Mapbox Standard style and its configuration API, Mapbox Streets, and Mapbox's satellite and terrain tiles. Replace them with other sources.
- Mapbox's services behind plugins and SDKs, such as geocoding, search, and directions. Replace each with another API, such as Mapricot's places and routing.
If you use React, react-map-gl has a MapLibre entry point, and the switch is a few lines: see MapLibre React.
Questions
Do I need a Mapbox token to use MapLibre? No. MapLibre has no token. You only need a key if the tiles or APIs you use ask for one, and Mapricot's map tiles don't.
Is MapLibre a drop-in replacement for Mapbox GL JS? Close to it: MapLibre started from Mapbox GL JS's code, and the classes and methods share names. The style, any mapbox:// URLs, CSS class names, and Mapbox's own services need changing, as above, and features Mapbox added after the fork need checking one by one.
Does MapLibre cost anything? The library is free and open source. The tiles behind it may not be: Mapricot's are free, with no key.