MapLibre GL JS Knowledge Patch
Use this skill when implementing, migrating, or debugging MapLibre GL JS code whose behavior depends on recent APIs, runtime requirements, event semantics, sources, projections, or rendering changes.
Determine the installed package version from the project manifest before applying version-specific advice. Prefer public map, source, and render-argument APIs over inheritance or internal transform details.
Reference index
| Reference | Read when working with |
|---|---|
| Camera, globe, and location | Camera orientation, bounds fitting, globe queries, terrain elevation, geolocation, and URL hashes |
| Controls, markers, and UI | Popups, marker appearance, accessibility, dragging, and keyboard behavior |
| Events, types, and errors | Subscriptions, typed custom events, event classes, style lifecycle, and network failures |
| Packaging and runtime | ESM migration, workers, CSP, browser targets, WebGL setup, sanitization, and build artifacts |
| Sources, tiles, and requests | Vector and GeoJSON sources, overscaling, request transforms, validation, raster data, and image updates |
| Styles, projections, and rendering | Expressions, projections, custom layers, terrain, layer properties, shaders, and style images |
Breaking migration essentials
Import the ESM distribution correctly
The v6 package is ESM-only. Replace default imports with namespace or named imports:
import * as maplibregl from 'maplibre-gl';
// or
import {Map, setWorkerUrl} from 'maplibre-gl';
For direct browser loading, use a module script and the .mjs distribution:
<script type="module">
import {Map} from 'https://unpkg.com/maplibre-gl@^6.0.0/dist/maplibre-gl.mjs';
</script>
The final v6 browser build resolves its ESM worker as a module URL, including for cross-origin CDN use, and does not require the old CSP-specific bundle or a worker-src blob: exception. In a bundled application, call setWorkerUrl() once because a bundler may not preserve the worker URL relationship.
Meet the runtime requirements
V6 output targets ES2022 and requires WebGL 2. Update browsers and build tooling or transpile at the application boundary when needed. Handle unavailable WebGL through the map's error event.
map.on('error', handleMapError);
In v5, put context settings under canvasContextAttributes; do not pass former top-level options such as antialias or preserveDrawingBuffer.
const map = new Map({
container: 'map',
canvasContextAttributes: {
antialias: true,
preserveDrawingBuffer: true,
failIfMajorPerformanceCaveat: true,
contextType: 'webgl2'
}
});
Do not chain event registration
Evented.on() returns a Subscription, not the evented object. Register listeners separately and retain a subscription only when it must be removed.
const moveSubscription = map.on('move', onMove);
map.on('zoom', onZoom);
moveSubscription.unsubscribe();
In v6, discriminate event objects by event.type, not instanceof. Event classes and their generic maps are implementation-facing type structure, while the type field is the stable identification mechanism.
Stop depending on Map inheritance
V6 Map composes a Camera and forwards its public API; it no longer extends Camera. Replace inheritance checks and access to map.transform with public map methods. transform.getMatrixForModel is removed.
Update GeoJSON source calls
GeoJSONSource.setData() accepts only the data argument and no longer returns the source. Remove the old completion flag and method chains:
source.setData(nextData);
Nested objects in GeoJSON properties now round-trip as objects and use the internal __$json__ serialized prefix. Do not depend on the former unsupported representation.
Resolve missing style images with the resolver
A styleimagemissing listener cannot satisfy the current request by calling addImage() in v6. Register a synchronous or asynchronous resolver; an asynchronous resolver must add the image before its promise settles.
map.setMissingStyleImageResolver(async (id) => {
const image = await generateImage(id);
map.addImage(id, image);
});
Keep styleimagemissing only for observing images that remain unresolved.
Rename promoted APIs and shader directives
The v5 experimentalZoomLevelsToOverscale option becomes zoomLevelsToOverscale in v6. Because slicing can change rendering and queryRenderedFeatures() results, set it explicitly to undefined when retaining the former overscaling behavior is important.
const map = new Map({
container: 'map',
zoomLevelsToOverscale: undefined
});
Replace #pragma mapbox with #pragma maplibre in shared shader code.
High-value current APIs
Transform requests asynchronously
setTransformRequest accepts an async callback. Return RequestParameters.referrerPolicy when tile requests require a particular referrer policy.
map.setTransformRequest(async (url) => ({
url,
referrerPolicy: 'no-referrer'
}));
Use whole-layer opacity deliberately
fill-layer-opacity and line-layer-opacity composite uniformly across a complete layer. line-layer-opacity avoids opacity accumulation at overlapping line geometry; alpha embedded in line-color still stacks.
paint: {'line-layer-opacity': 0.5}
Type application events intentionally
For v5 TypeScript projects, declaration-merge MapEventType to define application events. Newer typed event-name checks may require explicit casts for names outside the map event map.
declare module 'maplibre-gl' {
interface MapEventType {
'app:ready': {type: 'app:ready'; payload: string};
}
}
map.fire('app:ready' as any);
Feed decoded images directly
When an overlay is already decoded, pass an HTMLImageElement, HTMLCanvasElement, ImageBitmap, or ImageData to ImageSource.updateImage() in {image} to avoid another network request.
map.getSource('overlay').updateImage({image: decodedImage});
Coordinate custom layers with projections
Use getProjectionData from the custom-layer render arguments rather than removed transform internals. During globe-to-Mercator transitions, read the live defaultProjectionData.projectionTransition value so custom drawing eases with built-in layers.
Diagnostic checklist
When code compiles but behaves differently after an upgrade, check these areas before adding workarounds:
- Confirm whether the application is on v5 or v6 and whether the consuming toolchain supports ESM and ES2022.
- Check worker URL handling separately for direct browser ESM and bundler output.
- Inspect listener return values, event names, and event discrimination.
- Check whether overscaling, projection, or camera snapping changed the queried or displayed geometry.
- Validate source updates, nested GeoJSON properties, terrain configuration, and custom source handling.
- Distinguish whole-layer opacity from color alpha and built-in projection data from custom matrix assumptions.
- Read the topic reference before changing APIs; several corrections remove the need for older compatibility workarounds.