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 --version to 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 any accessToken option.
  • 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 load unless you set the worker's URL, which is what the setWorkerUrl lines 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, and grayscale styles at https://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.