Skip to content
Documentation
Skill

/maplibre-v6-migration

Upgrading a MapLibre GL JS app from v5 to v6 — the ESM-only build, the removed default export, CommonJS require() breakage, the styleimagemissing/setMissingStyleImageResolver change, the removal of the internal map.transform, the MapDataEvent →

BOOST
From plugin
maplibre-agent-skills
15511 skills
Install
$ npx -y skills add maplibre/maplibre-agent-skills --skill maplibre-v6-migration --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/maplibre-v6-migration

Context preview

The summary Claude sees to decide when to auto-load this skill.

Upgrading a MapLibre GL JS app from v5 to v6 — the ESM-only build, the removed default export, CommonJS require() breakage, the styleimagemissing/setMissingStyleImageResolver change, the removal of the internal map.transform, the MapDataEvent →

SKILL.md

maplibre-v6-migration.SKILL.md
name: maplibre-v6-migration
description: Upgrading a MapLibre GL JS app from v5 to v6 — the ESM-only build, the removed default export, CommonJS require() breakage, the styleimagemissing/setMissingStyleImageResolver change, the removal of the internal map.transform, the MapDataEvent → MapSourceDataEvent/MapStyleDataEvent split, and the bundler-only setWorkerUrl() requirement. Use when a v5 app breaks after upgrading to v6, or before pinning a v6 install.
status: verified

MapLibre GL JS v5 → v6 Migration

MapLibre GL JS v6 (released 2026-07-22) removed several things v5 code relied on: the UMD/CSP browser bundles, the default export, CommonJS support, the internal `map.transform`, and the `MapDataEvent` type. Most models' training data predates v6. So the natural-sounding answer to "how do I do X" is usually the v5 answer, and it breaks on v6.

**Primary reference:** the [MapLibre GL JS v5→v6 migration guide](https://maplibre.org/maplibre-gl-js/docs/guides/v5-to-v6-migration-guide/). This skill adds the gaps a model tends to fill with v5-era defaults. If the guide and this skill disagree, follow the guide and [report it](https://github.com/maplibre/maplibre-agent-skills/issues/new?template=ai-failure-report.md).

When to Use This Skill

  • Upgrading an existing MapLibre GL JS v5 app to v6, or debugging a map that "used to work" after a dependency update
  • Writing one of the seven patterns below: a CDN `<script>` tag, an import statement, a `require()` call, a `styleimagemissing` handler, code that reads `map.transform`, a typed `data`/`dataloading`/`dataabort` handler, or a bundler setup (Vite, webpack, esbuild, Rspack, Rollup)
  • Debugging errors like `ERR_PACKAGE_PATH_NOT_EXPORTED`, a blank map after a CDN update, a sprite icon that never appears, a TypeScript error naming `MapDataEvent`, or a worker-loading error that blocks render

**Don't use this skill to pad an unrelated answer.** It covers seven narrow breaking changes, not general v6 best practice. A question about sources, layers, styling, or terrain gets a normal, focused answer with no migration reminders attached.

1. CDN script tag: ESM-only now

v6 removed the UMD bundle and the separate CSP build. There's no `dist/maplibre-gl.js` for a plain `<script src="...">` tag anymore. Only the ESM build, `dist/maplibre-gl.mjs`, remains, and it needs `type="module"`.

<!-- ❌ v5 — dist/maplibre-gl.js no longer exists in v6 -->
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>

<!-- ✅ v6 — ESM build, type="module" required -->
<script type="module">
  import maplibregl from 'https://unpkg.com/maplibre-gl@^6.0.0/dist/maplibre-gl.mjs';
  const map = new maplibregl.Map({ container: 'map', style: '...' });
</script>

**Always pin an explicit major** (`@^6.0.0`, or a specific version). Never use `@latest` or a bare unversioned specifier — an unpinned CDN URL breaks the page silently the moment the next major version publishes. This already happened at the v5→v6 boundary.

2. No default export — named or namespace import only

v5's `import maplibregl from 'maplibre-gl'` (default import) no longer works in v6.

// ❌ v5 — default export removed
import maplibregl from 'maplibre-gl';

// ✅ v6 — namespace import
import * as maplibregl from 'maplibre-gl';

// ✅ v6 — or import only what you use
import { Map, NavigationControl } from 'maplibre-gl';

This applies whether the code is new or converted from a Mapbox GL JS snippet (`import mapboxgl from 'mapbox-gl'`). Don't carry the default-import shape over either way.

3. No CommonJS — `require('maplibre-gl')` throws

`maplibre-gl`'s `package.json` `exports` field has only an `"import"` condition in v6, not `"require"`. So `require('maplibre-gl')` throws `ERR_PACKAGE_PATH_NOT_EXPORTED` in plain Node. That's expected behavior, not a bug in the caller's setup.

// ❌ throws ERR_PACKAGE_PATH_NOT_EXPORTED in v6
const maplibregl = require('maplibre-gl');

// ✅ convert the file to ESM (named/namespace import — see item 2)
import { Map } from 'maplibre-gl';

// ✅ or, if the caller must stay CommonJS, load it dynamically
const { Map } = await import('maplibre-gl');

This section is only about a bare Node `require()` call. If a bundler is involved (webpack, Vite, etc.) and `require()` still fails, that's a separate ESM-interop config issue.

4. `styleimagemissing` no longer resolves the request — use `setMissingStyleImageResolver`

In v5, listening for `styleimagemissing` and calling `map.addImage()` synchronously from the handler would supply the missing icon. In v6, `styleimagemissing` is **notify-only**. Calling `addImage` from the handler no longer resolves the pending request.

// ❌ v5 pattern — no longer resolves the request in v6
map.on('styleimagemissing', (e) => {
  map.addImage(e.id, generateIcon(e.id));
});

// ✅ v6 — register a resolver
map.setMissingStyleImageResolver((id) => {
  return generateIcon(id); // or return a Promise
});

Use this whenever a style references `icon-image` names that aren't in the sprite sheet and need to be generated or fetched at runtime.

5. `map.transform` is gone — use the public Camera API

v6 refactored `Map` to compose a `Camera` instead of extending it. `Map` now extends `Evented` directly and forwards the camera API. The internal `map.transform` property was removed as part of that change.

// ❌ v5 — reaching into the internal transform
const { zoom, bearing, pitch } = map.transform;
const center = map.transform.center;

// ✅ v6 — public accessors
const zoom = map.getZoom();
const bearing = map.getBearing();
const pitch = map.getPitch();
const center = map.getCenter();

**There's no general public replacement for the raw projection/view matrix.** It was never exposed on `Map` itself, in v5 or v6 — it only ever lived on `map.transform`. The one place a matrix is still available is inside a custom layer's `render()` callback, via the callback's arguments (`Custom

Read more
Ships withmaplibre-agent-skills

Curated guidance for AI assistants building MapLibre applications — covering ecosystem and open-source best practices. Agent skills are markdown files that AI coding assistants read as context.

Get the whole plugin

Other skills on maplibre-agent-skills.