Skill 06 · Mapbox Google Maps Migration
Subchapter 6.6
AGENTS.mdMarkdown9 KBView on GitHub
Quick reference for migrating from Google Maps Platform to Mapbox GL JS with API equivalents and patterns.
| Aspect | Google Maps | Mapbox GL JS |
|---|---|---|
| Coordinates | {lat, lng} objects | [lng, lat] arrays |
| Philosophy | Imperative (objects) | Declarative (data-driven) |
| Rendering | DOM elements | WebGL (much faster) |
| Performance | Slow with 500+ markers | Fast with 10,000+ points |
| Initialization | new google.maps.Map() | new mapboxgl.Map() |
✅ Install mapbox-gl package
✅ Get Mapbox access token
✅ Swap coordinate order (lat,lng → lng,lat)
✅ Replace Google Maps API with Mapbox equivalents
✅ Use Symbol layers for 100+ markers (not HTML markers)
✅ Add clustering for 500+ points
✅ Update geocoding to Mapbox Geocoding API
✅ Test all functionality
// Google Maps
const map = new google.maps.Map(document.getElementById('map'), {
center: { lat: 37.7749, lng: -122.4194 },
zoom: 12
});
// Mapbox GL JS
mapboxgl.accessToken = 'pk.your_token';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [-122.4194, 37.7749], // Note: [lng, lat]
zoom: 12
});// Google Maps
const marker = new google.maps.Marker({
position: { lat: 37.7749, lng: -122.4194 },
map: map
});
// Mapbox (equivalent approach)
const marker = new mapboxgl.Marker().setLngLat([-122.4194, 37.7749]).addTo(map);// ❌ Google Maps: DOM-based (slow with 500+ markers)
locations.forEach((loc) => {
new google.maps.Marker({
position: { lat: loc.lat, lng: loc.lng },
map: map
});
});
// ✅ Mapbox: WebGL-based (fast with 10,000+ points)
map.addSource('points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: locations.map((loc) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [loc.lng, loc.lat] }
}))
}
});
map.addLayer({
id: 'points',
type: 'symbol',
source: 'points',
layout: {
'icon-image': 'marker-15'
}
});Performance Note: Google Maps renders ALL markers as DOM elements (even with Data Layer). Mapbox uses WebGL for Symbol/Circle layers = 10-100x faster for large datasets.
// Google Maps (requires MarkerClusterer library)
import MarkerClusterer from '@googlemaps/markerclustererplus';
const clusterer = new MarkerClusterer(map, markers);
// Mapbox (built-in)
map.addSource('points', {
type: 'geojson',
data: geojson,
cluster: true,
clusterRadius: 50
});// Google Maps
const infowindow = new google.maps.InfoWindow({
content: '<h3>Title</h3>'
});
infowindow.open(map, marker);
// Mapbox
const popup = new mapboxgl.Popup().setHTML('<h3>Title</h3>').setLngLat([-122.4194, 37.7749]).addTo(map);
// Or attach to marker
marker.setPopup(popup);// Google Maps
marker.addListener('click', () => {
/* ... */
});
map.addListener('click', (e) => {
const lat = e.latLng.lat();
const lng = e.latLng.lng();
});
// Mapbox
marker.on('click', () => {
/* ... */
});
map.on('click', (e) => {
const [lng, lat] = [e.lngLat.lng, e.lngLat.lat];
});// Google Maps
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: '1600 Amphitheatre Parkway' }, (results) => {
map.setCenter(results[0].geometry.location);
});
// Mapbox
fetch(
`https://api.mapbox.com/search/geocode/v6/forward?q=1600+Amphitheatre+Parkway&access_token=${mapboxgl.accessToken}`
)
.then((r) => r.json())
.then((data) => {
const [lng, lat] = data.features[0].geometry.coordinates;
map.setCenter([lng, lat]);
});// Google Maps
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: 'San Francisco',
destination: 'Los Angeles',
travelMode: 'DRIVING'
},
(result) => {
/* ... */
}
);
// Mapbox
fetch(
`https://api.mapbox.com/directions/v5/mapbox/driving/-122.4194,37.7749;-118.2437,34.0522?access_token=${mapboxgl.accessToken}`
)
.then((r) => r.json())
.then((data) => {
const route = data.routes[0].geometry;
// Display route on map
});// Google Maps
const polygon = new google.maps.Polygon({
paths: coordinates,
map: map
});
// Mapbox
map.addSource('polygon', {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [coordinates] // Note: Array of arrays
}
}
});
map.addLayer({
id: 'polygon',
type: 'fill',
source: 'polygon',
paint: {
'fill-color': '#088',
'fill-opacity': 0.5
}
});Most common migration bug:
// ❌ Google Maps order (lat, lng)
{ lat: 37.7749, lng: -122.4194 }
// ✅ Mapbox order (lng, lat)
[-122.4194, 37.7749]
// Remember: Mapbox follows GeoJSON standard (longitude first)Mapbox is significantly faster for:
When Mapbox wins:
When Google Maps might be better:
// Google Maps (limited styling)
const styledMapType = new google.maps.StyledMapType([{ elementType: 'geometry', stylers: [{ color: '#242f3e' }] }]);
// Mapbox (full control)
map.setStyle('mapbox://styles/mapbox/dark-v11');
// Or create custom styles in Mapbox StudioMapbox Styles:
streets-v12 - Standard streetsoutdoors-v12 - Hiking/outdoorlight-v11 / dark-v11 - Minimalsatellite-v9 / satellite-streets-v12 - ImageryGoogle Maps: Create marker for each store, add click listeners, show info windows Mapbox: Use Symbol layer + click events + popups (much faster for 100+ stores)
Google Maps: DirectionsRenderer Mapbox: Fetch route from Directions API, add as Line layer
Google Maps: HeatmapLayer (DOM-based) Mapbox: Heatmap layer (WebGL-based, much faster)
Google Maps:
Mapbox:
Token setup:
// Store in environment variables
mapboxgl.accessToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN;Checklist:
Phase 1: Setup
Phase 2: Core Migration
Phase 3: Features
Phase 4: Optimization
Easy migrations (mostly drop-in replacements):
Requires rethinking (but worth it):
Consider staying with Google Maps if: