All skills
mapbox avatar

/mapbox-web-performance-patterns

@f5ae7de official
by mapboxmapbox/mapbox-agent-skills80 stars
17

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.

Use this Skill: https://skilld.dev/gh/mapbox/mapbox-agent-skills/mapbox-web-performance-patterns

This session only. Nothing lands on disk.

AGENTS.md

≈1.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Mapbox GL JS Performance Optimization Guide

Quick reference for optimizing Mapbox GL JS applications. Prioritized by impact: 🔴 Critical → 🟡 High Impact → 🟢 Optimization.

🔴 Critical Performance Patterns (Fix First)

1. Eliminate Initialization Waterfalls

Impact: Saves 500ms-2s on initial load

Problem: Sequential loading (map → data → render) Solution: Parallel data fetching

// ❌ Sequential: 1.5s total
map.on('load', async () => {
  const data = await fetch('/api/data'); // Waits for map first
});

// ✅ Parallel: ~1s total
const dataPromise = fetch('/api/data'); // Starts immediately
const map = new mapboxgl.Map({...});
map.on('load', async () => {
  const data = await dataPromise; // Already fetching
});

Key principle: Start all data fetches immediately, don't wait for map load.

2. Bundle Size Optimization

Impact: 200-500KB savings, faster load times

Critical actions:

  • Use dynamic imports for large features: const geocoder = await import('mapbox-gl-geocoder')
  • Code-split by route/feature
  • Avoid importing entire Mapbox GL JS if only using specific features
  • Use CSS splitting for mapbox-gl.css

Size targets: <500KB initial bundle, <200KB per route

🟡 High Impact Patterns

3. Marker Performance

Impact: Smooth rendering with many markers

Decision tree:

  • < 100 markers: HTML markers (new mapboxgl.Marker()) - OK
  • 100-10,000 markers: Symbol layers - GPU-accelerated, much faster
  • 10,000+ markers: Symbol layers + clustering required
  • 100,000+ markers: Vector tiles with server-side clustering
// ✅ For 100+ markers: Use symbol layer, not HTML markers
map.addLayer({
  id: 'points',
  type: 'symbol',
  source: 'points',
  layout: { 'icon-image': 'marker' }
});

// ✅ For 10,000+ markers: Add clustering
map.addSource('points', {
  type: 'geojson',
  data: geojson,
  cluster: true,
  clusterRadius: 50 // Relative to tile dimensions (512 = full tile width)
});

4. Data Loading Strategy

Impact: Faster rendering, lower memory

Decision tree:

  • < 5MB GeoJSON: Load directly as GeoJSON source
  • > 5MB GeoJSON: Use vector tiles instead
  • Dynamic data: Implement viewport-based loading
  • Static data: Embed small datasets, fetch large ones

Viewport-based loading pattern:

map.on('moveend', () => {
  const bounds = map.getBounds();
  fetchDataInBounds(bounds).then((data) => {
    map.getSource('data').setData(data);
  });
});

Warning: setData() triggers a full re-parse in a web worker. For small datasets updated frequently, use source.updateData() (requires dynamic: true) for partial updates. For large datasets, switch to vector tiles.

5. Event Handler Optimization

Impact: Prevents jank during interactions

Rules:

  • Debounce search/geocoding: 300ms minimum
  • Throttle move/zoom events: 100ms for analytics, 16ms for UI updates (move fires ~60fps)
  • Use once() for one-time events
  • Remove event listeners on cleanup
// ✅ Debounce expensive operations
const debouncedSearch = debounce((query) => {
  geocode(query);
}, 300);

// ✅ Throttle frequent events
const throttledUpdate = throttle(() => {
  updateAnalytics(map.getCenter());
}, 100);

6. Memory Management

Critical for SPAs and long-running apps

Always cleanup on unmount:

// ✅ Remove map and all resources
map.remove(); // Removes all event listeners, sources, layers

// ✅ Cancel pending requests
controller.abort();

// ✅ Clear references
markers.forEach((m) => m.remove());
markers = [];

🟢 Optimization Patterns

7. Layer Management

Rules:

  • Use feature state instead of removing/re-adding layers for hover/selection
  • Batch style changes: Use map.once('idle', callback) after multiple changes
  • Hide layers with visibility: 'none' instead of removing
  • Minimize layer count: Combine similar layers with data-driven styling where possible

8. Rendering Optimization

Key patterns:

  • Set maxzoom on sources to avoid over-fetching tiles
  • Use generateId: true on GeoJSON sources to enable feature state (auto-assigns feature IDs)
  • Use promoteId to use an existing data property as the feature ID (alternative to generateId)
  • To fully skip collision work on a symbol layer, set BOTH 'icon-allow-overlap': true AND 'icon-ignore-placement': true (plus text equivalents if using text)
  • Avoid enabling preserveDrawingBuffer or antialias unless specifically needed

Quick Decision Guide

Slow initial load? → Check for waterfalls (data loading), optimize bundle size Jank with many markers? → Switch to symbol layers + clustering at 100+ markers Memory leaks in SPA? → Add proper cleanup (map.remove()) Slow with large data? → Use vector tiles, viewport loading Sluggish interactions? → Debounce/throttle event handlers High memory usage? → Use feature state instead of layer churn, check for listener leaks

Performance Testing

Measure what matters:

  • Time to Interactive (TTI): < 2s on 3G
  • First Contentful Paint (FCP): < 1s
  • Bundle size: < 500KB initial
  • Memory: Stable over time (no leaks)

Key API for measurement: map.isStyleLoaded() returns true when the style and all resources are fully loaded. Use map.once('idle') to detect when all rendering is complete.

Tools: Chrome DevTools Performance tab, Lighthouse, Bundle analyzers (webpack-bundle-analyzer, vite-bundle-visualizer)

Anti-Patterns to Avoid

  • Loading data after map initialization (waterfall)
  • Using HTML markers for 100+ points
  • Not clustering 10,000+ markers
  • Loading entire GeoJSON files > 5MB without vector tiles
  • Not debouncing search/geocoding
  • Forgetting to call map.remove() in SPAs
  • Adding/removing layers frequently (use feature state)
  • Not code-splitting large features
  • Calling setData() frequently on large GeoJSON sources (use vector tiles instead)

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    The skill provides a comprehensive set of performance optimization patterns and documentation for Mapbox GL JS applications. It focuses on legitimate development practices such as parallel data loading, bundle size optimization, and efficient marker rendering. No security risks or malicious patterns were detected.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer6mo

    1/2 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at f5ae7de. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 5 hours ago.

Activeupdated 2 months ago
  • Performance
  • mapbox
  • web
  • rendering
  • bundle-size
  • markers
  • geojson
  • memory
  • optimization

README badge

README badge for mapbox/mapbox-agent-skills/mapbox-web-performance-patterns

Provides performance optimization patterns for Mapbox GL JS applications, covering initialization waterfalls, bundle size, marker rendering, and memory management. Addresses critical issues like parallel data loading, symbol layers for large feature sets, and clustering strategies, with actionable code examples and performance thresholds.

Generated from the current SKILL.md.

Does this skill cover Mapbox GL JS only, or other Mapbox libraries?
This skill focuses on Mapbox GL JS web applications. It does not cover native mobile SDKs or server-side optimization.
What are the main performance bottlenecks this skill addresses?
The skill prioritizes initialization waterfalls (sequential data loading), bundle size, marker rendering (HTML vs symbol layers), and memory management. It covers the most impactful issues first, then optional optimizations.
When should I switch from HTML markers to symbol layers?
Use HTML markers for fewer than 100 markers. Switch to GPU-accelerated symbol layers for 100-10,000 markers, and add clustering for 10,000+ markers.
Does this skill provide code examples?
Yes. The skill includes concrete before/after code examples for initialization waterfalls, bundle optimization, marker strategies, and clustering patterns.

Generated from the current SKILL.md. These answers refresh after source changes.