References
Data LoadingAlso bundled
AGENTS19 chapters · 62 min
Skills
Chapter 19 of 19
Performance optimization patterns for Mapbox GL JS web applications.
3 minutes · 684 words · 21 sections
This skill provides performance optimization guidance for building fast, efficient Mapbox applications. Patterns are prioritized by impact on user experience, starting with the most critical improvements.
Performance philosophy: These aren’t micro-optimizations. They show up as waiting time, jank, and repeat costs that hit every user session.
Performance issues are prioritized by their impact on user experience:
Problem: Sequential loading creates cascading delays where each resource waits for the previous one.
Note: Modern bundlers (Vite, Webpack, etc.) and ESM dynamic imports automatically handle code splitting and library loading. The primary waterfall to eliminate is data loading - fetching map data sequentially instead of in parallel with map initialization.
// ❌ BAD: Data loads AFTER map initializes
async function initMap() {
const map = new mapboxgl.Map({
container: 'map',
accessToken: MAPBOX_TOKEN,
style: 'mapbox://styles/mapbox/streets-v12'
});
// Wait for map to load, THEN fetch data
map.on('load', async () =>
Timeline: Map init (0.5s) → Data fetch (1s) = 1.5s total
// ✅ GOOD: Data fetch starts immediately
async function initMap() {
// Start data fetch immediately (don't wait for map)
const dataPromise = fetch('/api/data').then((r) => r.json());
const map = new mapboxgl.Map({
container: 'map'
Timeline: Max(map init, data fetch) = ~1s total
// ✅ Set exact center/zoom so the map fetches the right tiles immediately
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [-122.4194, 37.7749],
zoom: 13
});
// Use 'idle' to know when the initial viewport is fully rendered
// (all tiles, sprites, and other resources are loaded; no transitions in progress)
map.once
If you know the exact area users will see first, setting center and zoom upfront avoids the map starting at a default view and then panning/zooming to the target, which wastes tile fetches.
// ✅ Load critical features first, defer others
const map = new mapboxgl.Map({
/* config */
});
map.on('load', () => {
// 1. Add critical layers immediately
addCriticalLayers(map);
// 2. Defer secondary features
// Note: Standard style 3D buildings can be toggled via config:
// map.setConfigProperty('basemap', 'show3dObjects', false);
requestIdleCallback
Impact: Significant reduction in time-to-interactive, especially when deferring terrain and 3D layers
Problem: Large bundles delay time-to-interactive on slow networks.
Note: Modern bundlers (Vite, Webpack, etc.) automatically handle code splitting for framework-based applications. The guidance below is most relevant for optimizing what gets bundled and when.
// ❌ BAD: Inline massive style JSON (can be 500+ KB)
const style = {
version: 8,
sources: {
/* 100s of lines */
},
layers: [
/* 100s of layers */
]
};
// ✅ GOOD: Reference Mapbox-hosted styles
const map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/streets-v12' // Fetched on demand
Impact: Reduces initial bundle by 30-50% when moving from inlined to hosted styles
Problem: Too many markers causes slow rendering and interaction lag.
// ❌ BAD: 5,000 HTML markers = 5+ second render, janky pan/zoom
restaurants.forEach((restaurant) => {
const marker = new mapboxgl.Marker()
.setLngLat([restaurant.lng, restaurant.lat])
.setPopup(new mapboxgl.Popup().setHTML(restaurant.name))
.addTo(map);
});Result: 5,000 DOM elements, slow interactions, high memory
// ✅ GOOD: GPU-accelerated rendering, smooth at 10,000+ features
map.addSource('restaurants', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: restaurants.map((r) => ({
type: 'Feature',
geometry: { type: 'Point'
Performance: 10,000 features render in <100ms
// ✅ GOOD: 50,000 markers → ~500 clusters at low zoom
map.addSource('restaurants', {
type: 'geojson',
data: restaurantsGeoJSON,
cluster: true,
clusterMaxZoom: 14, // Stop clustering at zoom 15
clusterRadius: 50 // Radius relative to tile dimensions (512 = full tile width)
});
Impact: 50,000 markers at 60 FPS with smooth interaction
When building a Mapbox application, verify these optimizations in order:
moveend)map.on('error', …) (or visible error UI) so style/tile/token failures are not silentFirst-pass agent code often ships a map with no map.on('error'), no map.remove(), and a tiny point set that never exercises cluster: true. Production demos need error visibility, teardown, and realistic scale.
// Measure initial load time
console.time('map-load');
map.on('load', () => {
console.timeEnd('map-load');
// isStyleLoaded() returns true when style, sources, tiles, sprites, and models are all loaded
console.log('Style loaded:', map.isStyleLoaded());
});
// Monitor frame rate
let
Target metrics:
For detailed patterns on specific topics, load the corresponding reference file:
references/data-loading.md — GeoJSON vs Vector Tiles decision matrix, viewport-based loading, progressive loading, vector tiles for large datasetsreferences/interactions.md — Debounce/throttle events, optimize feature queries, batch DOM updatesreferences/memory.md — Map cleanup patterns, popup/marker reuse, feature state vs dynamic layersreferences/mobile.md — Device detection, mobile-optimized layers, touch interaction, constructor optionsreferences/layers-styles.md — Consolidate layers with data-driven styling, simplify expressions, zoom-based visibilityInstall this repository
npx skills add mapbox/mapbox-agent-skills/plugin marketplace add mapbox/mapbox-agent-skillsSkills install per repository, not per chapter — the CLI has no documented per-skill form, so we do not print one.
Performance optimization patterns for Mapbox GL JS web applications. Covers initialization waterfalls, bundle size, rendering performance, memory management, and web optimization. Prioritized by impact on user experience.
The verbatim description from this skill’s front matter — the string an agent matches on to decide whether to load it.
main, last pushed 7 August 2026.SKILL.md, not by matching a directory convention. One layout observed: skills/*/SKILL.md.h1 and no skipped levels:.claude-plugin/marketplace.json by Mapbox Plugin Marketplace, declaring 1 plugin. It is read for editorial metadata only — never as the skill index, which is always the repository tree./mapbox/mapbox-agent-skills.md.md7 files · 25 KB
Everything this skill ships beside its prose. All of it is set here, as subchapters of chapter 19.
Documentation the agent loads on demand, rather than up front.
Everything else published alongside the skill.