Agent Native

mapbox-web-performance-patterns

This skill guides optimizing Mapbox GL JS web applications for performance, covering initialization waterfalls, bundle size, marker rendering, and memory…

by MapboxOfficial publisherNot reviewed

Identity proven by GitHub's verified organisation badge (mapbox)

Agents
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
License
MIT (permissive)
Pin
f71cd72pinned Oct 9, 2026
Context cost
about 2,700 tokenswhen the skill loads
GitHub stars
80on mapbox/mapbox-agent-skills
Scripts
None found

What it does

“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 author's description, from SKILL.md

Generated summary from the skill's own text

This skill guides optimizing Mapbox GL JS web applications for performance, covering initialization waterfalls, bundle size, marker rendering, and memory management. It prioritizes fixes by impact on user experience, from critical issues like sequential data loading to optimization techniques like clustering and feature state. The skill includes code examples, performance thresholds, and measurement patterns for achieving targets like sub-2-second load times and 60 FPS interactions.

When to use it

  • Building Mapbox web maps with thousands of points or features
  • Optimizing slow map initialization or jank during pan and zoom
  • Reducing bundle size for maps on slow networks or mobile devices
  • Reducing memory usage in long-running or single-page map applications

When not to

  • Maps with only a few dozen markers where basic HTML markers suffice
  • Applications not using Mapbox GL JS library
  • Server-side map rendering or non-interactive map tiles

Install

These commands install from the publisher's repository and need no account.

skills CLI

npx skills add mapbox/mapbox-agent-skills --skill mapbox-web-performance-patterns

Installs from the repository's default branch, which may have changed since the commit we checked.

GitHub CLI 2.90 or later

gh skill install mapbox/mapbox-agent-skills mapbox-web-performance-patterns@f71cd72b8d8937b06db440538bd65559eb792c49

Installs the commit we checked (f71cd72).

Copy by hand

Download the folder at f71cd72 and put it in .claude/skills/mapbox-web-performance-patterns for Claude Code and in .agents/skills/mapbox-web-performance-patterns for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.

Skill folder on GitHub at f71cd72

Claude Code plugin

/plugin install mapbox@claude-plugins-official

The publisher's plugin in Claude's official marketplace. It may hold more than this skill, and we have not pinned it.

Copy for your agent

A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.

Prompt

Install the agent skill "mapbox-web-performance-patterns" by Mapbox.

Source, pinned to commit f71cd72b8d8937b06db440538bd65559eb792c49:
https://github.com/mapbox/mapbox-agent-skills/tree/f71cd72b8d8937b06db440538bd65559eb792c49/skills/mapbox-web-performance-patterns

1. Download that folder at exactly this commit. Do not use the default branch.
2. Copy every file unchanged, keeping the folder structure, into .claude/skills/mapbox-web-performance-patterns/ for Claude Code and .agents/skills/mapbox-web-performance-patterns/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist.
3. Do not edit the files, and do not run any script in the folder unless I ask you to.
4. When you are done, list the files you copied and where you put them.

Listing and checks: https://www.agentnative.dev/skills/mapbox/mapbox-web-performance-patterns

SKILL.md at the pin

---
name: mapbox-web-performance-patterns
description: 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.
---

# 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

```javascript
// ❌ 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 () => {
    const data = await fetch('/api/data'); // Waterfall!
    map.addSource('data', { type: 'geojson', data: await data.json() });
  });
}
```

**Timeline:** Map init (0.5s) → Data fetch (1s) = **1.5s total**

### Solution: Parallel Data Loading

```javascript
// ✅ 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',
    accessToken: MAPBOX_TOKEN,
    style: 'mapbox://styles/mapbox/streets-v12'
  });

  // Data is ready when map loads
  map.on('load', async () => {
    const data = await dataPromise;
    map.addSource('data', { type: 'geojson', data });
    map.addLayer({
      id: 'data-layer',
      type: 'circle',
      source: 'data'
    });
  });
}
```

**Timeline:** Max(map init, data fetch) = **~1s total**

### Set Precise Initial Viewport

```javascript
// ✅ 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('idle', () => {
  console.log('Initial viewport fully rendered');
});
```

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

```javascript
// ✅ 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(
    () => {
      addTerrain(map);
      addCustom3DLayers(map); // For classic styles with custom fill-extrusion layers
    },
    { timeout: 2000 }
  );

  // 3. Defer analytics and non-visual features
  setTimeout(() => {
    initializeAnalytics(map);
  }, 3000);
});
```

**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

```javascript
// ❌ 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
});

// ✅ OR: Store large custom styles externally
const map = new mapboxgl.Map({
  style: '/styles/custom-style.json' // Loaded separately
});
```

**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

```javascript
// ❌ 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

### Solution: Use Symbol Layers (GeoJSON)

```javascript
// ✅ 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', coordinates: [r.lng, r.lat] },
      properties: { name: r.name, type: r.type }
    }))
  }
});

map.addLayer({
  id: 'restaurants',
  type: 'symbol',
  source: 'restaurants',
  layout: {
    'icon-image': 'restaurant',
    'icon-size': 0.8,
    'text-field': ['get', 'name'],
    'text-size': 12,
    'text-offset': [0, 1.5],
    'text-anchor': 'top'
  }
});

// Click handler (one listener for all features)
map.on('click', 'restaurants', (e) => {
  const feature = e.features[0];
  new mapboxgl.Popup().setLngLat(feature.geometry.coordinates).setHTML(feature.properties.name).addTo(map);
});
```

**Performance:** 10,000 features render in <100ms

### Solution: Clustering for High Density

```javascript
// ✅ 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)
});

// Cluster circle layer
map.addLayer({
  id: 'clusters',
  type: 'circle',
  source: 'restaurants',
  filter: ['has', 'point_count'],
  paint: {
    'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
    'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
  }
});

// Cluster count label
map.addLayer({
  id: 'cluster-count',
  type: 'symbol',
  source: 'restaurants',
  filter: ['has', 'point_count'],
  layout: {
    'text-field': '{point_count_abbreviated}',
    'text-size': 12
  }
});

// Individual point layer
map.addLayer({
  id: 'unclustered-point',
  type: 'circle',
  source: 'restaurants',
  filter: ['!', ['has', 'point_count']],
  paint: {
    'circle-color': '#11b4da',
    'circle-radius': 6
  }
});
```

**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

```javascript
// 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 frameCount = 0;
map.on('render', () => frameCount++);
setInterval(() => {
  console.log('FPS:', frameCount);
  frameCount = 0;
}, 1000);

// Check memory usage (Chrome DevTools -> Performance -> Memory)
```

**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

From mapbox/mapbox-agent-skills at f71cd72, shown as plain text under its license: MIT (permissive). The license text is in LICENSE.md.

View SKILL.md on GitHub at f71cd72

Files

8 files, 37 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.

Skill folder on GitHub at f71cd72
All 20 skills from Mapbox

More from Mapbox

  • mapbox-android-patterns

    This skill provides official patterns for integrating Mapbox Maps SDK v11 on Android, covering setup, map initialization, markers, user location display…

    Claude Code, Codex, Cursor +4 · about 2.5K tokens · pinned 6 days ago

  • mapbox-cartography

    This skill provides cartographic expertise for designing effective maps with Mapbox, covering visual hierarchy, color theory, typography, and zoom strategies.

    Claude Code, Codex, Cursor +4 · about 1.7K tokens · pinned 6 days ago

  • mapbox-data-visualization-patterns

    This skill teaches the agent to implement data visualization patterns on Mapbox maps, including choropleth maps, heat maps, 3D extrusions, and animated data…

    Claude Code, Codex, Cursor +4 · about 2.1K tokens · pinned 6 days ago

  • mapbox-flutter-patterns

    This skill teaches agents the standard patterns for integrating the Mapbox Maps Flutter SDK into iOS and Android apps written in Dart.

    Claude Code, Codex, Cursor +4 · about 2.3K tokens · pinned 6 days ago

  • mapbox-geospatial-operations

    This skill guides agents on selecting appropriate geospatial tools from the Mapbox MCP Server based on problem requirements.

    Claude Code, Codex, Cursor +4 · about 4.8K tokens · pinned 6 days ago

  • mapbox-google-maps-migration

    This skill guides developers migrating from Google Maps Platform to Mapbox GL JS by comparing APIs, translating patterns, and highlighting architectural…

    Claude Code, Codex, Cursor +4 · about 2.9K tokens · pinned 6 days ago

  • next-bundle-optimizer

    Audits Next.js applications to identify and reduce browser bundle size and initial-load work by analyzing routes, dependencies and client outputs.

    by VercelOfficial publisher

    Claude Code, Codex, Cursor +4 · about 2.3K tokens · pinned 6 days ago

  • next-cache-components-optimizer

    Optimize Next.js routes to display meaningful UI instantly on initial load or client navigation by encoding the optimization goal as a failing…

    by VercelOfficial publisher

    Claude Code, Codex, Cursor +4 · about 5.6K tokens · pinned 6 days ago

  • next-partial-prefetching-adoption

    Enables Partial Prefetching in Next.js 16.3+ apps and guides adoption through auditing Link prefetch behavior, preserving UI in tests or manually, and…

    by VercelOfficial publisher

    Claude Code, Codex, Cursor +4 · about 6.7K tokens · pinned 6 days ago

  • remotion-interactivity

    This skill teaches how to structure Remotion code so the Studio IDE recognizes and makes it interactive.

    by RemotionOfficial publisher

    Claude Code, Codex, Cursor +3 · about 2.4K tokens · pinned 6 days ago

  • remotion-interactivity

    This skill teaches how to structure Remotion video components to unlock Studio interactivity features like click selection, drag-and-drop, resizing, rotation…

    by RemotionOfficial publisher

    Claude Code, Codex, Cursor +3 · about 4.8K tokens · pinned 6 days ago

  • remotion-maps

    This skill enables creating map-based animations with Remotion using four different mapping libraries: Mapbox, MapLibre, MapTiler, or CesiumJS.

    by RemotionOfficial publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 240 tokens · pinned 6 days ago

The skills listed here are third-party software, collected from public GitHub repositories as a catalog of recent skills and provided as is. Agent Native does not write or control them and is not responsible for any harm, loss or damage caused by installing or using them. Labels and checks describe what we saw at one commit and are not a guarantee. Read a skill's files before you run it. Terms of use