npx skills add ...
npx skills add mapbox/mapbox-agent-skills --skill mapbox-google-maps-migration
npx skills add mapbox/mapbox-agent-skills --skill mapbox-google-maps-migration
Migration guide for developers moving from Google Maps Platform to Mapbox GL JS, covering API equivalents, pattern translations, and key differences
Comprehensive guidance for migrating from Google Maps Platform to Mapbox GL JS. Provides API equivalents, pattern translations, and strategies for successful migration.
.setMap(map)Key Insight: Mapbox treats everything as data + styling, not individual objects.
Key Differences:
{lat, lng}, Mapbox uses [lng, lat]| Google Maps | Mapbox GL JS | Notes |
|---|---|---|
map.setCenter(latLng) | map.setCenter([lng, lat]) | Coordinate order reversed |
map.getCenter() | map.getCenter() | Returns LngLat object |
map.setZoom(zoom) | map.setZoom(zoom) | Same behavior |
map.getZoom() | map.getZoom() | Same behavior |
map.panTo(latLng) | map.panTo([lng, lat]) | Animated pan |
map.fitBounds(bounds) | map.fitBounds([[lng,lat],[lng,lat]]) | Different bound format |
map.setMapTypeId(type) | map.setStyle(styleUrl) | Completely different approach |
map.getBounds() | map.getBounds() | Similar |
| Google Maps | Mapbox GL JS | Notes |
|---|---|---|
google.maps.event.addListener(map, 'click', fn) | map.on('click', fn) | Simpler syntax |
event.latLng | event.lngLat | Event property name |
'center_changed' | 'move' / 'moveend' | Different event names |
'zoom_changed' | 'zoom' / 'zoomend' | Different event names |
'bounds_changed' | 'moveend' | No direct equivalent |
'mousemove' | 'mousemove' | Same |
'mouseout' | 'mouseleave' | Different name |
Google Maps:
Mapbox GL JS:
Google Maps:
Mapbox GL JS (Equivalent Approach):
Mapbox GL JS (Data-Driven Approach - Recommended for 100+ points):
Performance Advantage: Google Maps renders all markers as DOM elements (even when using the Data Layer), which becomes slow with 500+ markers. Mapbox's circle and symbol layers are rendered by WebGL, making them much faster for large datasets (1,000-10,000+ points). This is a significant advantage when building applications with many points.
Identify all Google Maps features you use:
Start with basic map initialization:
new google.maps.Map() with new mapboxgl.Map()Prioritize by complexity:
Change event syntax:
google.maps.event.addListener() -> map.on()latLng -> lngLat)Take advantage of Mapbox features:
Always double-check coordinate order!
Never remove and re-add layers to update data — this reinitializes WebGL resources and causes a visible flash. Instead:
Consider staying with Google Maps if:
Remember: lng, lat order in Mapbox!
Works with:
The following reference files contain detailed migration guides for specific topics. Load them when working on those areas:
references/shapes-geocoding.md — Polygons, Polylines, Custom Icons, Geocodingreferences/directions-controls.md — Directions/Routing, Controlsreferences/clustering-styling.md — Clustering, Styling/Appearancereferences/data-performance.md — Data Updates, Performance, Common Migration Patterns (Store Locator, Drawing Tools, Heatmaps)references/api-services.md — API Services Comparison, Pricing, Plugins, Framework Integration, Testing, Migration ChecklistTo load a reference, read the file relative to this skill directory, e.g.:
mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12', // or satellite-v9, outdoors-v12
center: [-122.4194, 37.7749], // [lng, lat] - note the order!
zoom: 12
});const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map,
title: 'San Francisco',
icon: 'custom-icon.png'
});
// Remove marker
marker.setMap(null);// Create marker
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749])
.setPopup(new mapboxgl.Popup().setText('San Francisco'))
.addTo(map);
// Remove marker
marker.remove();const markers = locations.map(
(loc) =>
new google.maps.Marker({
position: { lat: loc.lat, lng: loc.lng },
map: map
})
);// Same object-oriented approach
const markers = locations.map((loc) => new mapboxgl.Marker().setLngLat([loc.lng, loc.lat]).addTo(map));// Add as GeoJSON source + layer (uses WebGL, not DOM)
map.addSource('points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: locations.map((loc) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [loc.lng, loc.lat] },
properties: { name: loc.name }
}))
}
});
map.addLayer({
id: 'points-layer',
type: 'circle', // or 'symbol' for icons
source: 'points',
paint: {
'circle-radius': 8,
'circle-color': '#ff0000'
}
});const infowindow = new google.maps.InfoWindow({
content: '<h3>Title</h3><p>Content</p>'
});
marker.addListener('click', () => {
infowindow.open(map, marker);
});// Option 1: Attach to marker
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749])
.setPopup(new mapboxgl.Popup().setHTML('<h3>Title</h3><p>Content</p>'))
.addTo(map);
// Option 2: On layer click (for data-driven markers)
map.on('click', 'points-layer', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const description = e.features[0].properties.description;
new mapboxgl.Popup().setLngLat(coordinates).setHTML(description).addTo(map);
});<!-- Replace Google Maps script -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css" rel="stylesheet" />// Google Maps
{ lat: 37.7749, lng: -122.4194 }
// Mapbox (REVERSED!)
[-122.4194, 37.7749]// Google Maps
map.on('click', (e) => {
console.log(e.latLng.lat(), e.latLng.lng());
});
// Mapbox
map.on('click', (e) => {
console.log(e.lngLat.lat, e.lngLat.lng);
});// Google Maps - immediate
const marker = new google.maps.Marker({ map: map });
// Mapbox - wait for load
map.on('load', () => {
map.addSource(...);
map.addLayer(...);
});// Google Maps
marker.setMap(null);
// Mapbox - must remove both
map.removeLayer('layer-id');
map.removeSource('source-id');// ✅ Update data in place (no flash)
map.getSource('stores').setData(newGeoJSON);
// ✅ Filter existing data (GPU-side, fastest)
map.setFilter('stores-layer', ['==', ['get', 'category'], 'coffee']);
// ❌ BAD: remove + re-add causes flash
map.removeLayer('stores-layer');
map.removeSource('stores');
map.addSource('stores', { ... });
map.addLayer({ ... });// GOOGLE MAPS
const map = new google.maps.Map(el, {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12
});
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map
});
google.maps.event.addListener(map, 'click', (e) => {
console.log(e.latLng.lat(), e.latLng.lng());
});
// MAPBOX GL JS
mapboxgl.accessToken = 'YOUR_TOKEN';
const map = new mapboxgl.Map({
container: el,
center: [-122.4194, 37.7749], // REVERSED!
zoom: 12,
style: 'mapbox://styles/mapbox/streets-v12'
});
const marker = new mapboxgl.Marker()
.setLngLat([-122.4194, 37.7749]) // REVERSED!
.addTo(map);
map.on('click', (e) => {
console.log(e.lngLat.lat, e.lngLat.lng);
});Load references/shapes-geocoding.md