mapbox-web-performance-patterns
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.
By mapbox · 2,027 installs
npx skills add mapbox/mapbox-agent-skills --skill mapbox-web-performance-patterns
Source repository · Upstream listing
Mapbox Performance Patterns Skill
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.
Priority Levels
Performance issues are prioritized by their impact on user experience:
🔴 Critical (Fix First) : Directly causes slow initial load or visible jank
🟡 High Impact : Noticeable delays or increased resource usage
🟢 Optimization : Incremental improvements for polish
🔴 Critical: Eliminate Initialization Waterfalls
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.
Anti Pattern: Sequential Data Loading
Timeline: Map init (0.5s) → Data fetch (1s) = 1.5s total
Solution: Parallel Data Loading
Timeline: Max(map init, data fetch) = ~1s total
Set Precise Initial Viewport
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.
Defer Non Critical Features
Impact: Significant reduction in time to interactive, especially when deferring terrain and 3D layers
🔴 Critical: Optimize Initial Bundle Size
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.
Style JSON Bundle Impact
Impact: Reduces initial bundle by 30 50% when moving from inlined to hosted styles
🟡 High Impact: Optimize Marker Count
Problem: Too many markers causes slow rendering and interaction lag.
Performance Thresholds
< 100 markers : HTML markers OK (Marker class)
100 10,000 markers : Use symbol layers (GPU accelerated)
10,000+ markers : Clustering recommended
100,000+ markers : Vector tiles with server side clustering
Anti Pattern: Thousands of HTML Markers
Result: 5,000 DOM elements, slow interactions, high memory
Solution: Use Symbol Layers (GeoJSON)
Performance: 10,000 features render in <100ms
Solution: Clustering for High Density
Impact: 50,000 markers at 60 FPS with smooth interaction
Summary: Performance Checklist
When building a Mapbox application, verify these optimizations in order:
🔴 Critical (Do First)
[ ] Load map library and data in parallel (eliminate waterfalls)
[ ] Use dynamic imports for map code (reduce initial bundle)
[ ] Defer non critical features (terrain, custom 3D layers, analytics)
[ ] Use symbol layers for 100 markers (not HTML markers)
[ ] Implement viewport based data loading for large datasets
🟡 High Impact
[ ] Debounce/throttle map event handlers (geocode inputs, moveend )
[ ] Optimize queryRenderedFeatures with layers filter and bounding box
[ ] Use GeoJSON for < 5 MB, vector tiles for 20 MB
[ ] Always call map.remove() on cleanup in SPAs / page teardown
[ ] Attach map.on('error', …) (or visible error UI) so style/tile/token failures are not silent
[ ] Reuse popup instances (don't create on every interaction)
[ ] Use feature state instead of dynamic layers for hover/selection
[ ] Cluster demos: generate enough points to stress clustering (thousands, not a few hundred)
Agent anti pattern: happy path only
First 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.
🟢 Optimization
[ ] Consolidate multiple layers with data driven styling
[ ] Add mobile specific optimizations (circle layers, disabled rotation)
[ ] Set minzoom/maxzoom on layers to avoid rendering at irrelevant zoom levels
[ ] Avoid enabling preserveDrawingBuffer or antialias unless needed
Measurement
Target metrics:
Time to Interactive: < 2 seconds on 3G
Frame Rate: 60 FPS during pan/zoom
Memory Growth: < 10 MB per hour of usage
Bundle Size: < 500 KB initial (map lazy loaded)
Reference Files
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 datasets
references/interactions.md — Debounce/throttle events, optimize feature queries, batch DOM updates
references/memory.md — Map cleanup patterns, popup/marker reuse, feature state vs dynamic layers
references/mobile.md — Device detection, mobile optimized layers, touch interaction, constructor options
references/layers styles.md — Consolidate layers with data driven styling, simplify expressions, zoom based visibility