---
title: "mapbox/mapbox-agent-skills"
description: "Agent Skills from mapbox/mapbox-agent-skills."
source: https://github.com/mapbox/mapbox-agent-skills
ref: main
license: MIT
licenseName: "MIT License"
canonical: https://skillsdocs.com/mapbox/mapbox-agent-skills
base: https://github.com/mapbox/mapbox-agent-skills/blob/main/
chapters: 19
inlined: 19
withheld: 0
words: 13849
updated: 2026-08-07T16:12:53Z
generator: "Skills Docs"
---

> **mapbox/mapbox-agent-skills** — every Agent Skill in this repository, inlined verbatim.
>
> Canonical HTML: https://skillsdocs.com/mapbox/mapbox-agent-skills
> Per-chapter Markdown: https://skillsdocs.com/mapbox/mapbox-agent-skills/<skill>.md
> Machine manifest: https://skillsdocs.com/mapbox/mapbox-agent-skills/.well-known/agent-skills/index.json
> JSON: https://skillsdocs.com/api/v1/books/mapbox/mapbox-agent-skills
> Install: `npx skills add mapbox/mapbox-agent-skills`
> Upstream: https://github.com/mapbox/mapbox-agent-skills @ `main`
> Licence: MIT
>
> Content is mirrored from GitHub and © its authors, served unmodified. Takedown: https://github.com/kyleledbetter/skillsdocs/issues/new?labels=takedown&title=Takedown+request

# mapbox/mapbox-agent-skills


- **Chapters:** 19
- **Inlined:** 19 (licence detected)
- **Words:** 13,849
- **Reading time:** 62 min
- **Stars:** 72

## Table of contents

1. [mapbox-android-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-android-patterns.md) — Official integration patterns for Mapbox Maps SDK on Android. Covers installation, adding markers, user location, custom data, styles, camera control, and feat…
2. [mapbox-cartography](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-cartography.md) — Expert guidance on map design principles, color theory, visual hierarchy, typography, and cartographic best practices for creating effective and beautiful maps…
3. [mapbox-data-visualization-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-data-visualization-patterns.md) — Patterns for visualizing data on maps including choropleth maps, heat maps, 3D visualizations, data-driven styling, and animated data. Covers layer types, colo…
4. [mapbox-flutter-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-flutter-patterns.md) — Official integration patterns for the Mapbox Maps Flutter SDK. Covers installation, iOS/Android platform setup, access token configuration, MapWidget initializ…
5. [mapbox-geospatial-operations](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-geospatial-operations.md) — Expert guidance on choosing the right geospatial tool based on problem type, accuracy requirements, and performance needs
6. [mapbox-google-maps-migration](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-google-maps-migration.md) — Migration guide for developers moving from Google Maps Platform to Mapbox GL JS, covering API equivalents, pattern translations, and key differences
7. [mapbox-ios-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-ios-patterns.md) — Official integration patterns for Mapbox Maps SDK on iOS. Covers installation, adding markers, user location, custom data, styles, camera control, and features…
8. [mapbox-location-grounding](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-location-grounding.md) — Compose Mapbox MCP tools to produce grounded, cited location-aware responses from live data instead of training data
9. [mapbox-maplibre-migration](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-maplibre-migration.md) — Guide for migrating from MapLibre GL JS to Mapbox GL JS, covering API compatibility, token setup, style configuration, and the benefits of Mapbox's official su…
10. [mapbox-mcp-devkit-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-mcp-devkit-patterns.md) — Integration patterns for Mapbox MCP DevKit Server in AI coding assistants. Covers setup, style management, token management, validation workflows, and document…
11. [mapbox-mcp-runtime-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-mcp-runtime-patterns.md) — Integration patterns for Mapbox MCP Server in AI applications and agent frameworks. Covers runtime integration with pydantic-ai, mastra, LangChain, and custom…
12. [mapbox-search-integration](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-search-integration.md) — Complete workflow for implementing Mapbox search in applications - from discovery questions to production-ready integration with best practices
13. [mapbox-search-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-search-patterns.md) — Expert guidance on choosing the right Mapbox search tool and parameters for geocoding, POI search, and location discovery
14. [mapbox-store-locator-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-store-locator-patterns.md) — Common patterns for building store locators, restaurant finders, and location-based search applications with Mapbox. Covers marker display, filtering, distance…
15. [mapbox-style-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-style-patterns.md) — Common style patterns, layer configurations, and recipes for typical mapping scenarios including restaurant finders, real estate, data visualization, navigatio…
16. [mapbox-style-quality](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-style-quality.md) — Expert guidance on validating, optimizing, and ensuring quality of Mapbox styles through validation, accessibility checks, and optimization. Use when preparing…
17. [mapbox-token-security](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-token-security.md) — Security best practices for Mapbox access tokens, including scope management, URL restrictions, rotation strategies, and protecting sensitive data. Use when cr…
18. [mapbox-web-integration-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-web-integration-patterns.md) — Official integration patterns for Mapbox GL JS across popular web frameworks (React, Vue, Svelte, Angular). Covers setup, lifecycle management, token handling,…
19. [mapbox-web-performance-patterns](https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-web-performance-patterns.md) — Performance optimization patterns for Mapbox GL JS web applications. Covers initialization waterfalls, bundle size, rendering performance, memory management, a…


## Front matter

_The repository README, verbatim except that relative links are resolved against https://github.com/mapbox/mapbox-agent-skills/blob/main/._

# Mapbox Agent Skills

**Comprehensive Agent Skills** that teach AI assistants how to build fast, beautiful, secure Mapbox applications across **Web, iOS, and Android** platforms. Covers performance optimization, cartographic design, framework integration, platform migration, search integration, geospatial operations, and security best practices.

## Quick Start

### Codex Plugin Local Testing

This repository includes a Codex plugin manifest at `plugins/mapbox/.codex-plugin/plugin.json` and a repo marketplace at `.agents/plugins/marketplace.json`.

Codex marketplaces currently expect a plugin subdirectory such as `plugins/mapbox`. To avoid maintaining a second tracked copy of every skill, keep editing the root `skills/` directory and generate the local plugin package before testing:

```bash
npm run build:codex-plugin
codex plugin marketplace add .
codex plugin marketplace list
```

Then restart Codex and install the `mapbox` plugin from the `Mapbox Agent Skills` marketplace.

The generated `plugins/mapbox/skills/` and `plugins/mapbox/.mcp.json` files are ignored by git.

### Open Plugins (Cursor, Codex, GitHub Copilot, Claude Code)

This repository conforms to the [Open Plugins specification](https://open-plugins.com/), making it installable in any conformant tool - Cursor, Codex, GitHub Copilot, Claude Code, and more.

Add the marketplace source:

```
/plugin marketplace add mapbox/mapbox-agent-skills
```

Then install the plugin:

```
/plugin install mapbox
```

### Claude Plugin (Claude Code)

Install as a Claude Code plugin to get skills and MCP servers in one step:

```
/plugin marketplace add mapbox/mapbox-agent-skills
```

Then install the plugin:

```
/plugin install mapbox
```

This adds all Mapbox skills and connects the `mapbox`, `mapbox-devkit`, and `mapbox-docs` MCP servers automatically.

### Skills CLI

Install all Mapbox Agent Skills:

```bash
npx skills add mapbox/mapbox-agent-skills
```

Install specific skills:

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

List available skills:

```bash
npx skills add mapbox/mapbox-agent-skills --list
```

> **💡 Pro tip:** These skills work great on their own, but they're even more powerful when combined with the [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server). Skills provide the expertise (performance patterns, design principles), while MCP tools provide the actions (create styles, generate previews). Together, they enable complete workflows from design to deployment.

## What are Agent Skills?

Agent Skills are folders containing instructions and resources that AI assistants (like Claude Code, Cursor, GitHub Copilot) can discover and use to perform tasks more effectively. Unlike tools (which provide actions) or prompts (which provide workflows), skills provide **domain expertise** - the "know-how" that helps AI make informed decisions.

Think of skills as giving your AI assistant a specialized education in Mapbox development best practices.

## Available Skills

**Platform Coverage:**

- 🌐 **Web**: React, Vue, Svelte, Angular, Next.js (Mapbox GL JS)
- 📱 **iOS**: Swift, SwiftUI, UIKit (Maps SDK for iOS)
- 📱 **Android**: Kotlin, Jetpack Compose, View system (Maps SDK for Android)

---

### 🗺️ mapbox-geospatial-operations

**Expert guidance on choosing between offline geometric tools and routing APIs for geospatial operations.**

Helps AI assistants select the right tool based on problem type (geometric vs routing), accuracy requirements, and performance needs. Features the "as the crow flies" vs "as the crow drives" decision framework.

**Use when:**

- Deciding between offline calculations and API calls
- Calculating distances (straight-line vs road distance)
- Checking if points are within an area
- Creating travel-time zones vs distance buffers
- Optimizing geospatial operations for performance
- Understanding when to use routing vs geometry

**Key topics:**

- Decision framework: geometric operations vs routing APIs
- Distance calculations (crow flies vs crow drives)
- Proximity and containment checks
- Area and shape operations
- Routing and navigation patterns
- Clustering and optimization
- Hybrid approaches (combining tools)
- Common anti-patterns to avoid
- Performance and scale considerations

**Tool categories covered:**

- Offline geometric: distance, bearing, buffer, centroid, area, containment
- Routing APIs: directions, matrix, optimization, isochrone, map matching

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-geospatial-operations/SKILL.md)

---

### 🔍 mapbox-search-integration

**Complete workflow for implementing Mapbox search in applications from discovery to production.**

Guides AI assistants through the entire search integration process: asking discovery questions, selecting the right product, implementing with best practices, and avoiding common pitfalls. Embodies collective knowledge from the Mapbox search team and customer learnings.

**Use when:**

- User needs to add search to their app
- Implementing location search or geocoding
- Need to ask the right questions about requirements
- Want production-ready search implementation
- Avoiding common integration mistakes

**Key workflow:**

1. **Discovery questions** - What to ask users (POIs? Addresses? Geographic scope? Platform?)
2. **Product selection** - Search Box API vs Geocoding API, SDK vs direct API
3. **Implementation patterns** - Complete code examples for web, iOS, Android
4. **Best practices** - Debouncing, session tokens, error handling, UX patterns
5. **Common pitfalls** - What goes wrong and how to avoid it

**Covers all platforms:**

- Web (Mapbox Search JS React/Web/Core, Vue, Angular, Next.js)
- iOS (Search SDK for iOS with UISearchController integration)
- Android (Search SDK for Android with SearchView integration)
- Node.js (Mapbox Search JS Core)

**Best practices from "the good parts":**

- ✅ Debouncing (prevent API spam)
- ✅ Session token management (cost optimization)
- ✅ Geographic filtering (better results)
- ✅ Error handling (all failure cases)
- ✅ Mobile UX (touch targets, keyboards)
- ✅ Accessibility (keyboard nav, ARIA)
- ✅ Caching strategies (high-volume apps)
- ✅ Token security (proper scoping)

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-search-integration/SKILL.md)

---

### 🔍 mapbox-search-patterns

**Expert guidance on choosing the right search tool and parameters for geocoding, POI search, and location discovery.**

Helps AI assistants select between search_and_geocode, category_search, and reverse_geocode tools, and optimize parameters like proximity, bbox, country, limit, and more.

**Use when:**

- Choosing between search tools (specific names vs categories)
- Setting up geocoding or POI search
- Optimizing search parameters for accuracy
- Implementing "near me" searches
- Building autocomplete functionality
- Troubleshooting search results

**Key topics:**

- Tool selection decision matrix (brands vs categories vs coordinates)
- Parameter guidance (proximity, bbox, country, limit, types, ETA)
- Common patterns ("near me", route-based search, multilingual)
- Anti-patterns to avoid (missing proximity, wrong tool choice)
- Performance optimization (minimizing API calls, appropriate limits)
- Combining search with geospatial operations

**Search tools covered:**

- search_and_geocode_tool: Specific places, addresses, brands
- category_search_tool: Generic categories, plural queries
- reverse_geocode_tool: Coordinates to addresses

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-search-patterns/SKILL.md)

---

### 🔄 mapbox-maplibre-migration

**Migration guide between Mapbox GL JS and MapLibre GL JS in both directions.**

Covers the fork history, API compatibility (~95% identical), token handling, style differences, and feature parity. Helps developers migrate in either direction or use both libraries together.

**Use when:**

- Migrating from Mapbox to MapLibre (or vice versa)
- Evaluating which library to use
- Understanding API compatibility
- Switching tile sources
- Combining MapLibre rendering with Mapbox APIs

**Key topics:**

- Fork history and license differences
- Package and import changes
- Token handling (Mapbox token vs no token)
- Style URL differences (mapbox:// vs OSM/custom)
- Feature parity comparison
- Plugin compatibility
- Migration in both directions
- When to choose each library
- Using both together

**Covers:**

- Step-by-step migration guides (both directions)
- API compatibility matrix (100% compatible vs minor differences)
- Common migration issues and solutions
- Decision guide (when to use which)
- Side-by-side code examples

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-maplibre-migration/SKILL.md)

---

### ⚡ mapbox-web-performance-patterns

**Performance optimization patterns for building fast, efficient Mapbox GL JS web applications.**

Covers initialization waterfalls, bundle size, rendering performance, memory management, and web optimization. Patterns are prioritized by impact on user experience (Critical → High Impact → Optimization).

**Use when:**

- Optimizing map load time and time-to-interactive
- Reducing bundle size or implementing code splitting
- Debugging slow rendering or janky interactions
- Managing thousands of markers or large datasets
- Optimizing for web browsers and low-end hardware
- Preventing memory leaks in long-running applications

**Key topics:**

- Eliminating initialization waterfalls (parallel loading, data fetching)
- Bundle size optimization (code splitting)
- Marker performance (HTML vs Canvas vs Symbol layers, clustering)
- Data loading strategies (GeoJSON vs vector tiles, viewport-based loading)
- Event handling optimization (debouncing, throttling)
- Memory management (cleanup patterns, feature state)
- Web-specific optimizations (browser performance, touch events)

**Based on:** Performance principles from [Vercel's react-best-practices](https://vercel.com/blog/introducing-react-best-practices) and Mapbox GL JS patterns

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-web-performance-patterns/SKILL.md)

---

### 🎨 mapbox-cartography

**Expert guidance on map design principles, color theory, visual hierarchy, typography, and cartographic best practices.**

**Use when:**

- Designing a new map style
- Choosing colors for map elements
- Making decisions about visual hierarchy
- Optimizing for specific use cases (navigation, data viz, etc.)
- Ensuring accessibility
- Creating themed maps (dark mode, vintage, etc.)

**Key topics:**

- Core cartographic principles (visual hierarchy, color theory)
- Typography best practices for maps
- Map context considerations (audience, platform, use case)
- Zoom level strategies
- Color palette templates
- Common mapping scenarios (restaurant finders, real estate, etc.)

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-cartography/SKILL.md)

---

### 🔧 mapbox-web-integration-patterns

**Official integration patterns for Mapbox GL JS across popular web frameworks and Web Components.**

Covers React, Vue, Svelte, Angular, Next.js, and framework-agnostic Web Components (Custom Elements) with proper lifecycle management, token handling, and search integration. Based on Mapbox's `create-web-app` scaffolding tool.

**Use when:**

- Setting up Mapbox GL JS in a new web project
- Integrating Mapbox into a specific web framework
- Building framework-agnostic Web Components
- Creating reusable component libraries
- Adding Mapbox Search functionality
- Implementing proper cleanup and lifecycle management
- Debugging map initialization issues in web apps
- Converting between frameworks

**Key topics:**

- Framework-specific patterns (React hooks, Vue composition API, Svelte stores, Angular services)
- Web Components (Custom Elements, Shadow DOM, reactive attributes)
- Token management (environment variables across frameworks)
- Lifecycle management and cleanup (preventing memory leaks)
- Mapbox Search JS integration
- Common mistakes and how to avoid them
- SSR handling (Angular Universal, Next.js)
- Framework-agnostic patterns for maximum portability

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-web-integration-patterns/SKILL.md)

---

### 📱 mapbox-ios-patterns

**Integration patterns for Mapbox Maps SDK on iOS with Swift, SwiftUI, UIKit, and mobile optimization.**

Covers Swift/SwiftUI integration, lifecycle management, token handling, offline maps, Navigation SDK, and battery/memory optimization for iOS devices.

**Use when:**

- Setting up Mapbox Maps SDK for iOS
- Integrating maps with SwiftUI or UIKit
- Managing token security in iOS apps
- Implementing offline map caching
- Adding turn-by-turn navigation
- Optimizing for battery life and memory
- Debugging crashes or performance issues on iOS

**Key topics:**

- SwiftUI UIViewRepresentable pattern
- UIKit lifecycle management (viewDidLoad, deinit)
- Token management (Info.plist, .xcconfig)
- Memory management and retain cycle prevention
- Offline map download and storage
- Navigation SDK integration
- Battery and network optimization
- Common iOS mistakes and solutions

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-ios-patterns/SKILL.md)

---

### 📱 mapbox-android-patterns

**Integration patterns for Mapbox Maps SDK on Android with Kotlin, Jetpack Compose, and mobile optimization.**

Covers Kotlin/Jetpack Compose integration, lifecycle management, token handling, offline maps, Navigation SDK, and battery/memory optimization for Android devices.

**Use when:**

- Setting up Mapbox Maps SDK for Android
- Integrating maps with Jetpack Compose or View system
- Managing token security in Android apps
- Implementing offline map caching
- Adding turn-by-turn navigation
- Optimizing for battery life and memory
- Debugging crashes or performance issues on Android

**Key topics:**

- Jetpack Compose AndroidView pattern
- Activity/Fragment lifecycle management
- Token management (BuildConfig, local.properties)
- Memory management and leak prevention
- Offline map download and storage
- Navigation SDK integration
- Battery and network optimization
- Common Android mistakes and solutions

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-android-patterns/SKILL.md)

---

### 📐 mapbox-style-patterns

**Common style patterns, layer configurations, and recipes for typical mapping scenarios.**

**Use when:**

- Starting a new map style for a specific use case
- Looking for layer configuration examples
- Implementing common mapping patterns
- Optimizing existing styles
- Need proven recipes for typical scenarios

**Key topics:**

- Restaurant/POI finder pattern
- Real estate map pattern
- Data visualization base map pattern
- Navigation/routing map pattern
- Dark mode / night theme pattern
- Layer optimization patterns
- Common modifications (3D buildings, terrain, custom markers)

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-style-patterns/SKILL.md)

---

### ✅ mapbox-style-quality

**Expert guidance on validating, optimizing, and ensuring quality of Mapbox styles.**

Covers validation, accessibility checks, and optimization techniques for production-ready styles.

**Use when:**

- Validating styles before production deployment
- Checking accessibility compliance (WCAG)
- Optimizing style file size and complexity
- Reviewing styles for common issues
- Setting up quality gates in CI/CD pipelines

**Key topics:**

- Style validation patterns
- Expression validation
- GeoJSON validation
- Color contrast checking (WCAG AA/AAA)
- Style optimization techniques
- Production readiness checklists

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-style-quality/SKILL.md)

---

### 🔐 mapbox-token-security

**Security best practices for Mapbox access tokens.**

Covers scope management, URL restrictions, rotation strategies, and incident response.

**Use when:**

- Creating new tokens
- Deciding between public vs secret tokens
- Setting up token restrictions
- Implementing token rotation
- Investigating security incidents
- Conducting security audits

**Key topics:**

- Token types and when to use them (public, secret, temporary)
- Scope management (principle of least privilege)
- URL restrictions and patterns
- Token storage and handling
- Rotation strategies
- Monitoring and auditing
- Incident response plans

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-token-security/SKILL.md)

---

### 📍 mapbox-store-locator-patterns

**Common patterns for building store locators, restaurant finders, and location-based search applications.**

Comprehensive guide covering marker display strategies, filtering, distance calculation, interactive lists synced with maps, and directions integration. Perfect for building any location-based finder application.

**Use when:**

- Building store locator or restaurant finder applications
- Displaying multiple locations on a map with interactive lists
- Need filtering by category, distance, or search term
- Calculate distances from user location
- Show location details in popups or panels
- Integrate directions to selected locations

**Key topics:**

- Marker strategies (HTML markers, symbol layers, clustering) based on location count
- Interactive list/map synchronization
- Search and category filtering
- User location and distance calculation (Haversine formula)
- Directions API integration
- Performance optimization (debouncing, lazy loading, virtual scrolling)
- Layout patterns (sidebar, overlay, mobile-responsive)

**Covers all marker strategies:**

- **< 100 locations**: HTML Markers with `mapboxgl.Marker()`
- **100-1000 locations**: Symbol layers with GeoJSON
- **> 1000 locations**: Clustering with aggregation

**Common use cases:**

- Store locators for retail chains
- Restaurant finders with category filters
- Office location directories
- Real estate property search
- Event venue finders
- Service provider locators

**Complete implementation:**

- GeoJSON data structure
- Map initialization and marker display
- Interactive listing component
- Search/filter functionality
- Distance calculation and sorting
- Popup/detail views
- Directions integration
- Responsive layouts

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-store-locator-patterns/SKILL.md)

---

### 🔄 mapbox-google-maps-migration

**Comprehensive migration guide for developers moving from Google Maps Platform to Mapbox GL JS.**

Covers API equivalents, coordinate order gotchas, pattern translations, and step-by-step migration strategies with side-by-side code examples.

**Use when:**

- Migrating an existing Google Maps application to Mapbox
- Evaluating Mapbox as a Google Maps alternative
- Learning Mapbox with Google Maps background
- Translating Google Maps patterns to Mapbox
- Troubleshooting migration issues

**Key topics:**

- API equivalents (Map, Marker, Polygon, InfoWindow, etc.)
- Critical differences (lng,lat vs lat,lng coordinate order)
- Event mapping and handling changes
- Markers: Object-oriented vs data-driven approaches
- Geocoding, directions, and clustering comparisons
- Performance considerations (WebGL vs DOM)
- Custom styling: Map types vs style URLs
- Step-by-step migration strategy and checklist
- When NOT to migrate guidance

**Covers all major features:**

- Map initialization and configuration
- Markers (individual and data-driven)
- Popups/info windows
- Polygons, polylines, shapes
- Custom icons and symbols
- Geocoding and reverse geocoding
- Directions/routing
- Clustering (built-in vs MarkerClusterer)
- Controls and UI elements
- Framework integration (React, Vue, etc.)

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-google-maps-migration/SKILL.md)

---

### 📊 mapbox-data-visualization-patterns

**Patterns for visualizing data on Mapbox maps.**

Covers choropleth maps, heat maps, clustering, bubble charts, 3D extrusions, and animated data layers. Includes data-driven styling with style expressions, color scales, and performance optimization.

**Use when:**

- Creating choropleth maps for statistics or demographics
- Building heat maps for point density visualization
- Clustering large datasets of point features
- Implementing 3D building or elevation extrusions
- Applying data-driven styles based on feature properties
- Animating data over time

**Key topics:**

- Visualization type selection (choropleth, heat map, clustering, bubble, 3D, flow)
- Style expressions for data-driven rendering
- Color scales and interpolation
- GeoJSON data structures
- Layer type selection (`fill`, `heatmap`, `circle`, `fill-extrusion`, `line`)
- Performance optimization for large datasets

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-data-visualization-patterns/SKILL.md)

---

### 🤖 mapbox-mcp-devkit-patterns

**Integration patterns for Mapbox MCP DevKit Server in AI coding assistants.**

Setup and usage guide for the [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server) — an MCP server for AI coding workflows. Covers style management, token creation, data validation, and documentation access through Claude Code, Cursor, and other AI assistants.

**Use when:**

- Setting up Mapbox MCP DevKit in Claude Code, Cursor, Claude Desktop, or VS Code
- Creating and iterating on map styles with AI assistance
- Generating scoped access tokens for different environments
- Validating GeoJSON, style expressions, or checking color contrast
- Looking up Mapbox documentation through your AI coding assistant

**Key topics:**

- Setup for Claude Code, Claude Desktop, Cursor, VS Code with Copilot
- Style management (create, update, preview, compare, validate)
- Token management with scoped permissions and URL restrictions
- GeoJSON and style expression validation
- Geographic utilities (bounding boxes, coordinate conversion)
- Dev/staging/prod environment setup

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-mcp-devkit-patterns/SKILL.md)

---

### 🌐 mapbox-mcp-runtime-patterns

**Integration patterns for Mapbox MCP Server in AI applications and agent frameworks.**

Patterns for integrating the [Mapbox MCP Server](https://github.com/mapbox/mcp-server) into production AI applications. Covers all major Python and TypeScript agent frameworks with working examples and correct API usage.

**Use when:**

- Integrating geospatial capabilities into AI agents (Pydantic AI, CrewAI, Smolagents, LangChain, Mastra)
- Building location-aware applications (real estate, food delivery, travel planning)
- Choosing between offline Turf.js tools vs. Mapbox API tools
- Optimizing geospatial operations (caching, batching, tool selection)
- Building Zillow-style, DoorDash-style, or TripAdvisor-style AI features

**Key topics:**

- Framework-specific patterns (Pydantic AI, CrewAI, Smolagents, LangChain, Mastra)
- Offline tools (distance, bearing, point-in-polygon, area — free, instant)
- API tools (directions, geocoding, isochrone, POI search — real-time data)
- Correct coordinate format (`{longitude, latitude}` objects)
- Tool description writing for accurate LLM tool selection
- Performance optimization and error handling

[View skill →](https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-mcp-runtime-patterns/SKILL.md)

---

## How Skills Work

### With Claude Code

Skills in this repository are automatically discovered by Claude Code when placed in:

```bash
# Install all skills
npx skills add mapbox/mapbox-agent-skills

# Or manually symlink (for development)
mkdir -p .claude
ln -s ~/git/mapbox-agent-skills/skills .claude/skills
```

Skills are automatically activated when relevant to your task.

### With Cursor

```bash
npx skills add mapbox/mapbox-agent-skills -a cursor
```

### With VS Code (GitHub Copilot)

```bash
npx skills add mapbox/mapbox-agent-skills -a vscode
```

### With Other AI Assistants

The `skills` CLI supports: OpenCode, Codex, Antigravity, and more. Run `npx skills add --help` for full list.

### Manual Installation

If your AI assistant doesn't support the `skills` CLI, you can copy skill files directly:

```bash
# For Claude Code — copy SKILL.md into .claude/skills/
mkdir -p .claude/skills/mapbox-web-performance-patterns
curl -o .claude/skills/mapbox-web-performance-patterns/SKILL.md \
  https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/SKILL.md

# For Codex, Copilot, and other AGENTS.md-based tools — copy to project root
curl -O https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/AGENTS.md
```

## Example Usage

Once installed, skills activate automatically when relevant:

### Performance Optimization

👤 User: "My Mapbox map with 5,000 restaurant markers is slow. How do I optimize it?"

🤖 Claude: [Uses mapbox-web-performance-patterns skill]

"Based on the marker performance thresholds:

- < 100 markers: HTML markers OK
- 100-1,000: Use Symbol layers (GeoJSON)
- 1,000-10,000: Clustering required
- > 10,000: Server-side clustering

For 5,000 markers, you need clustering. Here's the pattern..."

### Framework Integration

👤 User: "Set up Mapbox in my React app"

🤖 Claude: [Uses mapbox-web-integration-patterns skill]

"I'll use the React pattern with useRef + useEffect:

```jsx
import { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

function MapComponent() {
  const mapRef = useRef(null);
  const mapContainerRef = useRef(null);

  useEffect(() => {
    mapboxgl.accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;

    mapRef.current = new mapboxgl.Map({
      container: mapContainerRef.current,
      center: [-71.05953, 42.3629],
      zoom: 13
    });

    // CRITICAL: Cleanup to prevent memory leaks
    return () => {
      mapRef.current.remove();
    };
  }, []);

  return <div ref={mapContainerRef} style={{ height: '100vh' }} />;
}
```

Key points:

- Use useRef for both map instance and container
- Initialize in useEffect with empty deps []
- Always return cleanup function that calls map.remove()
- Never initialize map in render (causes infinite loops)"

### Map Design

👤 User: "Create a map style for a restaurant finder app"

🤖 Claude: [Uses mapbox-cartography + mapbox-style-patterns skills]

"For a restaurant finder, you need:

Design (from cartography skill):

- High contrast for restaurant markers
- Muted background (food photos will overlay)
- Clear street labels for navigation
- Mobile-optimized design

Style pattern (from style-patterns skill):
I'll apply the POI Finder pattern:

- Desaturated base map (#f5f5f5 background)
- Orange markers (#FF6B35) for visibility
- White roads on light gray background
- Simplified labels (only major streets)
  ..."

## How Skills Complement Mapbox MCP Server

These Agent Skills work together with the [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server):

| Component                    | Purpose               | Example                                 |
| ---------------------------- | --------------------- | --------------------------------------- |
| **Agent Skills** (this repo) | Provide expertise     | Performance patterns, design principles |
| **MCP Tools**                | Execute actions       | `create_style_tool`, `list_tokens_tool` |
| **MCP Prompts**              | Orchestrate workflows | `create-and-preview-style` workflow     |

**Example workflow:**

```
User: "Create a high-performance map for my restaurant finder"

1. [mapbox-cartography skill] → Understands restaurant maps need high contrast, muted background
2. [mapbox-token-security skill] → Creates token with only styles:read scope, URL restrictions
3. [mapbox-style-patterns skill] → Applies POI Finder pattern
4. [MCP style_builder_tool] → Generates style JSON
5. [MCP create_style_tool] → Creates style in Mapbox account
6. [mapbox-web-performance-patterns skill] → Recommends clustering for > 1,000 markers
7. [MCP preview_style_tool] → Generates preview link
```

## Examples

Want to see these skills in action? Check out the [`examples/`](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/) directory for both conversation transcripts and working code examples.

### 📝 Conversation Examples

Realistic conversation transcripts showing how AI assistants use the skills:

- [Web Performance Optimization](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/conversations/web-performance-optimization.md) - Optimizing a map with 5,000 markers using **mapbox-web-performance-patterns**
- [iOS SwiftUI Setup](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/conversations/ios-swiftui-setup.md) - Setting up Mapbox in SwiftUI using **mapbox-ios-patterns**
- [Android Jetpack Compose Setup](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/conversations/android-compose-setup.md) - Integrating Mapbox with Compose using **mapbox-android-patterns**
- [Restaurant Finder Design](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/conversations/restaurant-finder-design.md) - Designing a map style using **mapbox-cartography** + **mapbox-style-patterns**

### 💻 Working Code Examples

Complete, runnable applications following skill patterns:

**Web:**

- [react-map-basic](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/web/react-map-basic/) - Basic React integration with proper lifecycle management
- [performance-optimized](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/web/performance-optimized/) - Advanced patterns: clustering, parallel loading, throttling

**iOS:**

- [SwiftUIMapExample](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/ios/SwiftUIMapExample/) - SwiftUI integration with UIViewRepresentable pattern

**Android:**

- [ComposeMapExample](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/android/ComposeMapExample/) - Jetpack Compose integration with AndroidView pattern

Each example includes:

- ✅ Complete, working code
- ✅ Detailed README explaining patterns
- ✅ Comments highlighting key practices
- ✅ Setup and troubleshooting instructions

[**→ Browse all examples**](https://github.com/mapbox/mapbox-agent-skills/blob/main/examples/)

## Development

### Structure

Each skill follows this structure:

```
skill-name/
├── SKILL.md              # Main skill file (required)
│   ├── YAML frontmatter  # name, description
│   └── Markdown content  # Instructions and guidance
└── [optional files]      # Additional resources
```

### Creating Custom Skills

1. Create a new directory in `skills/`
2. Create `SKILL.md` with YAML frontmatter and instructions
3. Add reference materials (optional)
4. Test with Claude Code or Cursor
5. Submit a pull request

**Guidelines:**

- Keep instructions clear and actionable
- Provide concrete examples
- Include decision trees when applicable
- Reference official Mapbox documentation
- Test with real scenarios

### Testing

To test skills locally:

```bash
# Clone repo
git clone https://github.com/mapbox/mapbox-agent-skills.git
cd mapbox-agent-skills

# Install in Claude Code
npx skills add . -a claude-code

# Or symlink for development
mkdir -p .claude
ln -s $(pwd)/skills .claude/skills
```

Test with prompts like:

**Web:**

- "How do I optimize a Mapbox map with 50,000 markers?"
- "What's the best way to load large GeoJSON files?"
- "Set up Mapbox in my React app with proper cleanup"
- "Create a dark mode map style"

**iOS:**

- "Add Mapbox to my SwiftUI app"
- "How do I prevent memory leaks in my iOS map?"
- "Download offline maps for iOS"
- "Integrate Navigation SDK in my iOS app"

**Android:**

- "Integrate Mapbox with Jetpack Compose"
- "Handle lifecycle properly in my Android map Fragment"
- "Optimize battery usage for Android maps"
- "Set up offline regions for Android"

## Resources

**Mapbox Documentation:**

- [Mapbox Documentation](https://docs.mapbox.com)
- [Mapbox GL JS (Web)](https://docs.mapbox.com/mapbox-gl-js/guides/)
- [Maps SDK for iOS](https://docs.mapbox.com/ios/maps/guides/)
- [Maps SDK for Android](https://docs.mapbox.com/android/maps/guides/)
- [Mapbox Style Specification](https://docs.mapbox.com/style-spec/)
- [Mapbox Tutorials](https://docs.mapbox.com/help/tutorials/)

**Related Tools:**

- [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server)

## Contributing

We welcome contributions of new skills or improvements to existing ones! Please see our [Contributing Guide](https://github.com/mapbox/mapbox-agent-skills/blob/main/CONTRIBUTING.md) for detailed instructions on creating skills, testing, and submitting pull requests.

For questions or suggestions, please [open an issue](https://github.com/mapbox/mapbox-agent-skills/issues).

## License

MIT License - see [LICENSE](https://github.com/mapbox/mapbox-agent-skills/blob/main/LICENSE.md) file for details.

---

**Related Projects:**

- [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server) - MCP server with tools for Mapbox development
- [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) - JavaScript library for interactive web maps
- [Maps SDK for iOS](https://docs.mapbox.com/ios/maps/guides/) - Native iOS mapping SDK
- [Maps SDK for Android](https://docs.mapbox.com/android/maps/guides/) - Native Android mapping SDK
- [Vercel Agent Skills](https://github.com/vercel-labs/agent-skills) - Agent Skills for React and Next.js

---

<!-- chapter:begin slug=mapbox-android-patterns position=1 -->

## 1. mapbox-android-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-android-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-android-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (8), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/evals/evals.json
  - `references/annotations.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/references/annotations.md
  - `references/camera-styles.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/references/camera-styles.md
  - `references/compose.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/references/compose.md
  - `references/custom-data.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/references/custom-data.md
  - `references/interactions.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/references/interactions.md
  - `references/location-tracking.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-android-patterns/references/location-tracking.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-android-patterns
description: Official integration patterns for Mapbox Maps SDK on Android. Covers installation, adding markers, user location, custom data, styles, camera control, and featureset interactions. Based on official Mapbox documentation.
---

# Mapbox Android Integration Patterns

Official patterns for integrating Mapbox Maps SDK v11 on Android with Kotlin, Jetpack Compose, and View system.

**Use this skill when:**

- Installing and configuring Mapbox Maps SDK for Android
- Adding markers and annotations to maps
- Showing user location and tracking with camera
- Adding custom data (GeoJSON) to maps
- Working with map styles, camera, or user interaction
- Handling feature interactions and taps

**Official Resources:**

- [Android Maps Guides](https://docs.mapbox.com/android/maps/guides/)
- [API Reference](https://docs.mapbox.com/android/maps/api-reference/)
- [Example Apps](https://github.com/mapbox/mapbox-maps-android/tree/main/Examples)

---

## Installation & Setup

### Requirements

- Android SDK 21+
- Kotlin or Java
- Android Studio
- Free Mapbox account

### Step 1: Configure Access Token

Create `app/res/values/mapbox_access_token.xml`:

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
    <string name="mapbox_access_token" translatable="false"
        tools:ignore="UnusedResources">YOUR_MAPBOX_ACCESS_TOKEN</string>
</resources>
```

**Get your token:** Sign in at [mapbox.com](https://account.mapbox.com/access-tokens/)

### Step 1b: Internet permission (required)

Maps need network access. Include this in `AndroidManifest.xml` — agents often omit it and only list location permissions later:

```xml
<uses-permission android:name="android.permission.INTERNET" />
```

### Step 2: Add Maven Repository

In `settings.gradle.kts`:

```kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
        }
    }
}
```

### Step 3: Add Dependency

In module `build.gradle.kts`:

```kotlin
android {
    defaultConfig {
        minSdk = 21
    }
}

dependencies {
    implementation("com.mapbox.maps:android:11.18.1")
}
```

**For Jetpack Compose:**

```kotlin
dependencies {
    implementation("com.mapbox.maps:android:11.18.1")
    implementation("com.mapbox.extension:maps-compose:11.18.1")
}
```

---

## Map Initialization

### Jetpack Compose Pattern

**Basic map:**

```kotlin
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import com.mapbox.maps.extension.compose.*
import com.mapbox.maps.Style
import com.mapbox.geojson.Point

@Composable
fun MapScreen() {
    MapboxMap(
        modifier = Modifier.fillMaxSize()
    ) {
        // Initialize camera via MapEffect (Style.STANDARD loads by default)
        MapEffect(Unit) { mapView ->
            // Set initial camera position
            mapView.mapboxMap.setCamera(
                CameraOptions.Builder()
                    .center(Point.fromLngLat(-122.4194, 37.7749))
                    .zoom(12.0)
                    .build()
            )
        }
    }
}
```

**With ornaments:**

```kotlin
MapboxMap(
    modifier = Modifier.fillMaxSize(),
    scaleBar = {
        ScaleBar(
            enabled = true,
            position = Alignment.BottomStart
        )
    },
    compass = {
        Compass(enabled = true)
    }
) {
    // Style.STANDARD loads by default
}
```

### View System Pattern

**Layout XML (activity_map.xml):**

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.mapbox.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

**Activity:**

```kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.geojson.Point

class MapActivity : AppCompatActivity() {
    private lateinit var mapView: MapView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_map)

        mapView = findViewById(R.id.mapView)

        mapView.mapboxMap.setCamera(
            CameraOptions.Builder()
                .center(Point.fromLngLat(-122.4194, 37.7749))
                .zoom(12.0)
                .build()
        )

        mapView.mapboxMap.loadStyle(Style.STANDARD)
    }

    override fun onStart() {
        super.onStart()
        mapView.onStart()
    }

    override fun onStop() {
        super.onStop()
        mapView.onStop()
    }

    override fun onDestroy() {
        super.onDestroy()
        mapView.onDestroy()
    }
}
```

---

## Add Markers (Point Annotations)

Point annotations are the most common way to mark locations on the map.

**Jetpack Compose:**

```kotlin
MapboxMap(modifier = Modifier.fillMaxSize()) {
    MapEffect(Unit) { mapView ->
        // Load style first
        mapView.mapboxMap.loadStyle(Style.STANDARD)

        // Create annotation manager and add markers
        val annotationManager = mapView.annotations.createPointAnnotationManager()
        val pointAnnotation = PointAnnotationOptions()
            .withPoint(Point.fromLngLat(-122.4194, 37.7749))
            .withIconImage("custom-marker")
        annotationManager.create(pointAnnotation)
    }
}

// Note: Compose doesn't have declarative PointAnnotation component
// Markers must be added imperatively via MapEffect
```

**View System:**

```kotlin
// Create annotation manager (once, reuse for updates)
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()

// Create marker
val pointAnnotation = PointAnnotationOptions()
    .withPoint(Point.fromLngLat(-122.4194, 37.7749))
    .withIconImage("custom-marker")

pointAnnotationManager.create(pointAnnotation)
```

**Multiple markers:**

```kotlin
val locations = listOf(
    Point.fromLngLat(-122.4194, 37.7749),
    Point.fromLngLat(-122.4094, 37.7849),
    Point.fromLngLat(-122.4294, 37.7649)
)

val annotations = locations.map { point ->
    PointAnnotationOptions()
        .withPoint(point)
        .withIconImage("marker")
}

pointAnnotationManager.create(annotations)
```

---

## Show User Location (Display)

**Step 1: Add permissions to AndroidManifest.xml:**

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
```

**Step 2: Request permissions and show location:**

```kotlin
// Request permissions first (use ActivityResultContracts)

// Show location puck
mapView.location.updateSettings {
    enabled = true
    puckBearingEnabled = true
}
```

---

## Performance Best Practices

### Reuse Annotation Managers

```kotlin
// Don't create new managers repeatedly
// val manager = mapView.annotations.createPointAnnotationManager() // each call

// Create once, reuse
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()

fun updateMarkers() {
    pointAnnotationManager.deleteAll()
    pointAnnotationManager.create(markers)
}
```

### Batch Annotation Updates

```kotlin
// Create all at once
pointAnnotationManager.create(allAnnotations)

// Don't create one by one in a loop
```

### Lifecycle Management

```kotlin
// Always call lifecycle methods
override fun onStart() {
    super.onStart()
    mapView.onStart()
}

override fun onStop() {
    super.onStop()
    mapView.onStop()
}

override fun onDestroy() {
    super.onDestroy()
    mapView.onDestroy()
}
```

### Use Standard Style

```kotlin
// Standard style is optimized and recommended
Style.STANDARD

// Use other styles only when needed for specific use cases
Style.STANDARD_SATELLITE // Satellite imagery
```

---

## Troubleshooting

### Map Not Displaying

**Check:**

1. Token in `mapbox_access_token.xml`
2. Token is valid (test at mapbox.com)
3. Maven repository configured
4. Dependency added correctly
5. Internet permission in manifest

### Style Not Loading

```kotlin
mapView.mapboxMap.subscribeStyleLoaded { _ ->
    Log.d("Map", "Style loaded successfully")
    // Add layers and sources here
}
```

### Performance Issues

- Use `Style.STANDARD` (recommended and optimized)
- Limit visible annotations to viewport
- Reuse annotation managers
- Avoid frequent style reloads
- Call lifecycle methods (onStart, onStop, onDestroy)
- Batch annotation updates

---

## Reference Files

Load these references when you need detailed patterns for specific topics:

- **`references/compose.md`** -- Jetpack Compose: dependencies, token setup, MapboxMap, annotations with click, GeoJSON, MapEffect
- **`references/annotations.md`** -- Circle, Polyline, and Polygon annotation patterns
- **`references/location-tracking.md`** -- Camera follow user location + get current location once
- **`references/custom-data.md`** -- GeoJSON sources and layers: lines, polygons, points, update/remove
- **`references/camera-styles.md`** -- Camera control (set, animate, fit) + map styles (built-in and custom)
- **`references/interactions.md`** -- Featureset interactions, custom layer taps, long press, gestures

---

## Additional Resources

- [Android Maps Guides](https://docs.mapbox.com/android/maps/guides/)
- [API Reference](https://docs.mapbox.com/android/maps/api/11.18.1/)
- [Interactions Guide](https://docs.mapbox.com/android/maps/guides/user-interaction/interactions/)
- [Jetpack Compose Guide](https://docs.mapbox.com/android/maps/guides/using-jetpack-compose/)
- [Example Apps](https://github.com/mapbox/mapbox-maps-android/tree/main/Examples)
- [Migration Guide (v10 -> v11)](https://docs.mapbox.com/android/maps/guides/migrate-to-v11/)

<!-- chapter:end slug=mapbox-android-patterns -->

---

<!-- chapter:begin slug=mapbox-cartography position=2 -->

## 2. mapbox-cartography

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-cartography/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-cartography/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-cartography.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (4), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-cartography/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-cartography/evals/evals.json
  - `references/performance-testing.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-cartography/references/performance-testing.md
  - `references/scenarios.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-cartography/references/scenarios.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-cartography
description: Expert guidance on map design principles, color theory, visual hierarchy, typography, and cartographic best practices for creating effective and beautiful maps with Mapbox. Use when designing map styles, choosing colors, or making cartographic decisions.
---

# Mapbox Cartography Skill

This skill provides expert cartographic knowledge to help you design effective, beautiful, and functional maps using Mapbox.

## Core Cartographic Principles

### Visual Hierarchy

Maps must guide the viewer's attention to what matters most:

- **Most important**: POIs, user location, route highlights
- **Secondary**: Major roads, city labels, landmarks
- **Tertiary**: Minor streets, administrative boundaries
- **Background**: Water, land use, terrain

**Implementation:**

- Use size, color intensity, and contrast to establish hierarchy
- Primary features: high contrast, larger symbols, bold colors
- Background features: low contrast, muted colors, smaller text

### Color Theory for Maps

**Color Harmony:**

- **Analogous colors**: Use colors next to each other on color wheel (blue-green-teal) for cohesive designs
- **Complementary colors**: Use opposite colors (blue/orange, red/green) for high contrast emphasis
- **Monochromatic**: Single hue with varying saturation/brightness for elegant, minimal designs

**Color Psychology:**

- **Blue**: Water, trust, calm, professional (default for water bodies)
- **Green**: Parks, nature, growth, eco-friendly (vegetation, parks)
- **Red/Orange**: Urgent, important, dining (alerts, restaurants)
- **Yellow**: Caution, highlight, attention (warnings, selected items)
- **Gray**: Neutral, background, roads (infrastructure)

**Accessibility:**

- Ensure 4.5:1 contrast ratio for text (WCAG AA)
- Don't rely solely on color to convey information
- Test designs with colorblind simulators
- Avoid red/green combinations for critical distinctions

**Color Palette Templates:**

Light Theme (Day/Professional):

```json
{
  "background": "#f5f5f5",
  "water": "#a0c8f0",
  "parks": "#d4e7c5",
  "roads": "#ffffff",
  "buildings": "#e0e0e0",
  "text": "#333333"
}
```

Dark Theme (Night Mode):

```json
{
  "background": "#1a1a1a",
  "water": "#0d47a1",
  "parks": "#2e7d32",
  "roads": "#3a3a3a",
  "buildings": "#2d2d2d",
  "text": "#ffffff"
}
```

> **Road color rule for dark themes:** Roads must use neutral dark gray (`#3a3a3a`), visibly distinct from the background but not colored. Never style roads with amber, blue, or other hues — reserve color for app data layers (routes, markers). Colored base roads and colored data layers will compete visually. Local roads that blend into the background (`#1e1e1e` on `#1a1a1a`) create a "floating labels" problem where street names appear with no visible road beneath them.

High Contrast (Accessibility):

```json
{
  "background": "#000000",
  "water": "#0066ff",
  "parks": "#00ff00",
  "roads": "#ffffff",
  "buildings": "#808080",
  "text": "#ffffff"
}
```

Vintage/Retro:

```json
{
  "background": "#f4e8d0",
  "water": "#b8d4d4",
  "parks": "#c8d4a4",
  "roads": "#d4c4a8",
  "buildings": "#e4d4c4",
  "text": "#4a3828"
}
```

### Typography at Map Scale

**Font Selection:**

- **Sans-serif** (Roboto, Open Sans): Modern, clean, high legibility at small sizes - use for labels
- **Serif** (Noto Serif): Traditional, formal - use sparingly for titles or historic maps
- **Monospace**: Technical data, coordinates

**Text Sizing:**

```
Place labels (cities, POIs): 11-14px
Street labels: 9-11px
Feature labels (parks): 10-12px
Map title: 16-20px
Attribution: 8-9px
```

**Label Placement:**

- Point labels: Center or slightly offset (avoid overlap with symbol)
- Line labels: Follow line curve, repeat for long features
- Area labels: Center in polygon, sized appropriately
- Prioritize: Major features get labels first, minor features labeled if space allows

### Zoom Level Strategy

**Zoom 0-4** (World to Continent):

- Major country boundaries
- Ocean and sea labels
- Capital cities only

**Zoom 5-8** (Country to State):

- State/province boundaries
- Major cities
- Major highways
- Large water bodies

**Zoom 9-11** (Metro Area):

- City boundaries
- Neighborhoods
- All highways and major roads
- Parks and landmarks

**Zoom 12-15** (Neighborhood):

- All streets
- Building footprints
- POIs (restaurants, shops)
- Street names

> **Note:** Mapbox's hosted Streets style defaults to showing most POIs around zoom 14. For custom styles, start POIs at zoom 12 — this is the neighborhood scale where density is manageable and users are browsing. Zoom 14 is late; zoom 10 (metro-area scale) is far too early and creates severe icon clutter.

**Zoom 16-22** (Street Level):

- All detail
- House numbers
- Parking lots
- Fine-grained POIs

## Mapbox-Specific Implementation Guidance

### Style Layer Best Practices

**Layer Ordering (bottom to top):**

1. Background (solid color or pattern)
2. Landuse (parks, residential, commercial)
3. Water bodies (oceans, lakes, rivers)
4. Terrain/hillshade (if using elevation)
5. Buildings (3D or 2D footprints)
6. Roads (highways → local streets)
7. Borders (country, state lines)
8. Labels (place names, street names)
9. POI symbols
10. User-generated content (routes, markers)

> **Common mistake:** Developers often put their app's route line or active markers _below_ POI symbols, reasoning that "POIs must stay visible." This is backwards — user-generated content (your route, selected location, user position) is the most important layer and must render above everything, including POIs. A route line that covers a POI icon is acceptable; a route obscured by POI icons is not.

### Map Context Considerations

**Know Your Audience:**

- **General public**: Simplify, use familiar patterns (Google/Apple style)
- **Technical users**: Include more detail, technical layers, data precision
- **Domain experts**: Show specialized data, use domain-specific symbology

**Platform Considerations:**

- **Mobile**: Larger touch targets (44x44px minimum), simpler designs, readable at arm's length
- **Desktop**: Can include more detail, hover interactions, complex overlays
- **Print**: Higher contrast, larger text, consider CMYK color space
- **Outdoor/Bright**: Higher contrast, avoid subtle grays

**Use Case Optimization:**

- **Navigation**: Emphasize roads, clear hierarchy, route visibility
- **Data visualization**: Muted base map, let data stand out
- **Storytelling**: Guide viewer attention, establish mood with colors
- **Location selection**: Show POIs clearly, provide context
- **Analysis**: Include relevant layers, maintain clarity at different zooms

## Reference Files

For detailed guidance on specific topics, load these references as needed:

- `references/scenarios.md` — Common scenario guidance (Restaurant Finder, Real Estate, Data Visualization, Navigation)
- `references/performance-testing.md` — Performance optimization, testing checklist, and common mistakes to avoid

## When to Use This Skill

Invoke this skill when:

- Designing a new map style
- Choosing colors for map elements
- Making decisions about visual hierarchy
- Optimizing for specific use cases
- Troubleshooting visibility issues
- Ensuring accessibility
- Creating themed maps (dark mode, vintage, etc.)

<!-- chapter:end slug=mapbox-cartography -->

---

<!-- chapter:begin slug=mapbox-data-visualization-patterns position=3 -->

## 3. mapbox-data-visualization-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-data-visualization-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-data-visualization-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (8), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/evals/evals.json
  - `references/3d-extrusions.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/references/3d-extrusions.md
  - `references/animation.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/references/animation.md
  - `references/circles-lines.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/references/circles-lines.md
  - `references/clustering.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/references/clustering.md
  - `references/legends-use-cases.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/references/legends-use-cases.md
  - `references/performance.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-data-visualization-patterns/references/performance.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-data-visualization-patterns
description: Patterns for visualizing data on maps including choropleth maps, heat maps, 3D visualizations, data-driven styling, and animated data. Covers layer types, color scales, and performance optimization.
---

# Data Visualization Patterns Skill

Comprehensive patterns for visualizing data on Mapbox maps. Covers choropleth maps, heat maps, 3D extrusions, data-driven styling, animated visualizations, and performance optimization for data-heavy applications.

## When to Use This Skill

Use this skill when:

- Visualizing statistical data on maps (population, sales, demographics)
- Creating choropleth maps with color-coded regions
- Building heat maps or clustering for density visualization
- Adding 3D visualizations (building heights, terrain elevation)
- Implementing data-driven styling based on properties
- Animating time-series data
- Working with large datasets that require optimization

## Visualization Types

### Choropleth Maps

**Best for:** Regional data (states, counties, zip codes), statistical comparisons

**Pattern:** Color-code polygons based on data values

```javascript
map.on('load', () => {
  // Add data source (GeoJSON with properties)
  map.addSource('states', {
    type: 'geojson',
    data: 'https://example.com/states.geojson' // Features with population property
  });

  // Add fill layer with data-driven color
  map.addLayer({
    id: 'states-layer',
    type: 'fill',
    source: 'states',
    paint: {
      'fill-color': [
        'interpolate',
        ['linear'],
        ['get', 'population'],
        0,
        '#f0f9ff', // Light blue for low population
        500000,
        '#7fcdff',
        1000000,
        '#0080ff',
        5000000,
        '#0040bf', // Dark blue for high population
        10000000,
        '#001f5c'
      ],
      'fill-opacity': 0.75
    }
  });

  // Add border layer
  map.addLayer({
    id: 'states-border',
    type: 'line',
    source: 'states',
    paint: {
      'line-color': '#ffffff',
      'line-width': 1
    }
  });

  // Add hover effect with reusable popup
  const popup = new mapboxgl.Popup({
    closeButton: false,
    closeOnClick: false
  });

  map.on('mousemove', 'states-layer', (e) => {
    if (e.features.length > 0) {
      map.getCanvas().style.cursor = 'pointer';

      const feature = e.features[0];
      popup
        .setLngLat(e.lngLat)
        .setHTML(
          `
          <h3>${feature.properties.name}</h3>
          <p>Population: ${feature.properties.population.toLocaleString()}</p>
        `
        )
        .addTo(map);
    }
  });

  map.on('mouseleave', 'states-layer', () => {
    map.getCanvas().style.cursor = '';
    popup.remove();
  });
});
```

> **`step` vs `interpolate`:** The example above uses `interpolate` for smooth color gradients. For **discrete color buckets** (e.g., "low / medium / high"), use `['step', ['get', 'population'], '#f0f0f0', 500000, '#fee0d2', 2000000, '#fc9272', 10000000, '#de2d26']` instead. Prefer `step` when data has natural categories or when exact boundary values matter.

**Color Scale Strategies:**

```javascript
// Linear interpolation (continuous scale)
'fill-color': [
  'interpolate',
  ['linear'],
  ['get', 'value'],
  0, '#ffffcc',
  25, '#78c679',
  50, '#31a354',
  100, '#006837'
]

// Step intervals (discrete buckets)
'fill-color': [
  'step',
  ['get', 'value'],
  '#ffffcc',  // Default color
  25, '#c7e9b4',
  50, '#7fcdbb',
  75, '#41b6c4',
  100, '#2c7fb8'
]

// Case-based (categorical data)
'fill-color': [
  'match',
  ['get', 'category'],
  'residential', '#ffd700',
  'commercial', '#ff6b6b',
  'industrial', '#4ecdc4',
  'park', '#45b7d1',
  '#cccccc'  // Default
]
```

### Heat Maps

**Best for:** Point density, event locations, incident clustering

**Pattern:** Visualize density of points

```javascript
map.on('load', () => {
  // Add data source (points)
  map.addSource('incidents', {
    type: 'geojson',
    data: {
      type: 'FeatureCollection',
      features: [
        {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [-122.4194, 37.7749]
          },
          properties: {
            intensity: 1
          }
        }
        // ... more points
      ]
    }
  });

  // Add heatmap layer
  map.addLayer({
    id: 'incidents-heat',
    type: 'heatmap',
    source: 'incidents',
    maxzoom: 15,
    paint: {
      // Increase weight based on intensity property
      'heatmap-weight': ['interpolate', ['linear'], ['get', 'intensity'], 0, 0, 6, 1],
      // Increase intensity as zoom level increases
      'heatmap-intensity': ['interpolate', ['linear'], ['zoom'], 0, 1, 15, 3],
      // Color ramp for heatmap
      'heatmap-color': [
        'interpolate',
        ['linear'],
        ['heatmap-density'],
        0,
        'rgba(33,102,172,0)',
        0.2,
        'rgb(103,169,207)',
        0.4,
        'rgb(209,229,240)',
        0.6,
        'rgb(253,219,199)',
        0.8,
        'rgb(239,138,98)',
        1,
        'rgb(178,24,43)'
      ],
      // Adjust radius by zoom level
      'heatmap-radius': ['interpolate', ['linear'], ['zoom'], 0, 2, 15, 20],
      // Decrease opacity at higher zoom levels
      'heatmap-opacity': ['interpolate', ['linear'], ['zoom'], 7, 1, 15, 0]
    }
  });

  // Add circle layer for individual points at high zoom
  map.addLayer({
    id: 'incidents-point',
    type: 'circle',
    source: 'incidents',
    minzoom: 14,
    paint: {
      'circle-radius': ['interpolate', ['linear'], ['zoom'], 14, 4, 22, 30],
      'circle-color': '#ff4444',
      'circle-opacity': 0.8,
      'circle-stroke-color': '#fff',
      'circle-stroke-width': 1
    }
  });
});
```

## Best Practices

### Color Accessibility

```javascript
// Use ColorBrewer scales for accessibility
// https://colorbrewer2.org/

// Good: Sequential (single hue)
const sequentialScale = ['#f0f9ff', '#bae4ff', '#7fcdff', '#0080ff', '#001f5c'];

// Good: Diverging (two hues)
const divergingScale = ['#d73027', '#fc8d59', '#fee08b', '#d9ef8b', '#91cf60', '#1a9850'];

// Good: Qualitative (distinct categories)
const qualitativeScale = ['#e41a1c', '#377eb8', '#4daf4a', '#984ea3', '#ff7f00'];

// Avoid: Red-green for color-blind accessibility
// Use: Blue-orange or purple-green instead
```

### Error Handling

```javascript
// Handle missing or invalid data
map.on('load', () => {
  map.addSource('data', {
    type: 'geojson',
    data: dataUrl
  });

  map.addLayer({
    id: 'data-viz',
    type: 'fill',
    source: 'data',
    paint: {
      'fill-color': [
        'case',
        ['has', 'value'], // Check if property exists
        ['interpolate', ['linear'], ['get', 'value'], 0, '#f0f0f0', 100, '#0080ff'],
        '#cccccc' // Default color for missing data
      ]
    }
  });

  // Handle map errors
  map.on('error', (e) => {
    console.error('Map error:', e.error);
  });
});
```

## Data Size Rule

- **< 1 MB**: Use GeoJSON directly
- **1–10 MB**: Consider either GeoJSON or vector tiles depending on complexity
- **> 10 MB**: Use vector tiles (upload to Mapbox as tileset)

See [references/performance.md](references/performance.md) for implementation details.

## Reference Files

For additional visualization patterns, load the relevant reference file:

- **[references/clustering.md](references/clustering.md)** — Point clustering, custom cluster properties, clustering vs heatmap comparison
- **[references/3d-extrusions.md](references/3d-extrusions.md)** — 3D building extrusions, custom data sources, data-driven heights
- **[references/circles-lines.md](references/circles-lines.md)** — Circle/bubble maps, line data visualization, traffic flow styling
- **[references/animation.md](references/animation.md)** — Time-series animation, real-time data updates, smooth transitions
- **[references/performance.md](references/performance.md)** — Vector tiles vs GeoJSON, feature state, filtering, progressive loading
- **[references/legends-use-cases.md](references/legends-use-cases.md)** — Legend UI, data inspector, data preprocessing, election/COVID/real-estate examples

## Resources

- [Mapbox Expression Reference](https://docs.mapbox.com/style-spec/reference/expressions/)
- [ColorBrewer](https://colorbrewer2.org/) - Color scales for maps
- [Turf.js](https://turfjs.org/) - Spatial analysis
- [Simple Statistics](https://simple-statistics.github.io/) - Data classification
- [Data Visualization Tutorials](https://docs.mapbox.com/help/tutorials/#data-visualization)

<!-- chapter:end slug=mapbox-data-visualization-patterns -->

---

<!-- chapter:begin slug=mapbox-flutter-patterns position=4 -->

## 4. mapbox-flutter-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-flutter-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-flutter-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-flutter-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (4), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-flutter-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-flutter-patterns/evals/evals.json
  - `references/annotations.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-flutter-patterns/references/annotations.md
  - `references/platform-setup.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-flutter-patterns/references/platform-setup.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-flutter-patterns
description: Official integration patterns for the Mapbox Maps Flutter SDK. Covers installation, iOS/Android platform setup, access token configuration, MapWidget initialization, camera control, annotations with tap handling, user location, and loading GeoJSON. Based on official Mapbox documentation.
---

# Mapbox Flutter Integration Patterns

Official patterns for integrating the Mapbox Maps SDK for Flutter (mapbox_maps_flutter) on iOS and Android with Dart.

**Use this skill when:**

- Installing and configuring mapbox_maps_flutter in a Flutter app
- Setting the Mapbox access token the right way
- Initializing a `MapWidget` with camera / style options
- Adding annotations (points, circles, lines, polygons) and handling taps
- Showing the user location puck
- Loading GeoJSON from app assets
- Troubleshooting iOS build failures after adding Mapbox

**Official Resources:**

- [Flutter Maps Guides](https://docs.mapbox.com/flutter/maps/guides/)
- [API Reference on pub.dev](https://pub.dev/documentation/mapbox_maps_flutter/latest/)
- [Example App](https://github.com/mapbox/mapbox-maps-flutter/tree/main/example)

> Web and desktop are not supported — the Flutter SDK targets iOS and Android only.

---

## Installation & Setup

### Requirements

- Flutter SDK 3.22.3 / Dart 3.4.4+
- **iOS: deployment target 14.0 or higher**
- **Android: minSdk 21 or higher**
- Free Mapbox account

### Step 1: Add the dependency

```yaml
# pubspec.yaml
dependencies:
  mapbox_maps_flutter: ^2.0.0
```

```bash
flutter pub get
```

### Step 2: Bump the iOS deployment target to 14.0 (required)

**This is the single most common cause of iOS build failures after adding Mapbox.** The Flutter SDK requires **iOS 14.0** and will not compile on the Flutter default.

1. Open `ios/Runner.xcworkspace` in Xcode.
2. Select the **Runner** target → **General** → set **Minimum Deployments → iOS** to `14.0`.
3. If `ios/Podfile` exists, update the platform line too:

   ```ruby
   # ios/Podfile
   platform :ios, '14.0'
   ```

You do not need to worry about CocoaPods vs Swift Package Manager — `mapbox_maps_flutter` supports both and Flutter picks whichever your app is configured for.

### Step 3: iOS location permission

Add the purpose string to `ios/Runner/Info.plist`:

```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show your location on the map</string>
```

### Step 4: Android permissions

Add to `android/app/src/main/AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
```

### Step 5: Configure the access token

The recommended pattern is to pass the token via `--dart-define` at build/run time and set it on `MapboxOptions` before creating any `MapWidget`.

```bash
flutter run --dart-define=ACCESS_TOKEN=pk.your_token_here
```

```dart
// main.dart
import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

const accessToken = String.fromEnvironment('ACCESS_TOKEN');

void main() {
  MapboxOptions.setAccessToken(accessToken);
  runApp(const MaterialApp(home: MapScreen()));
}
```

Never hard-code tokens in source. For CI, pass `--dart-define=ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN`.

---

## Map Initialization

### Basic map

```dart
import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

class MapScreen extends StatelessWidget {
  const MapScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        key: const ValueKey('mapWidget'),
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-122.4194, 37.7749)),
          zoom: 12,
        ),
        styleUri: MapboxStyles.STANDARD,
      ),
    );
  }
}
```

### Grab the `MapboxMap` controller

```dart
class MapScreen extends StatefulWidget {
  const MapScreen({super.key});

  @override
  State<MapScreen> createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  MapboxMap? mapboxMap;

  void _onMapCreated(MapboxMap controller) {
    mapboxMap = controller;
  }

  @override
  Widget build(BuildContext context) {
    return MapWidget(
      key: const ValueKey('mapWidget'),
      onMapCreated: _onMapCreated,
      cameraOptions: CameraOptions(
        center: Point(coordinates: Position(-122.4194, 37.7749)),
        zoom: 12,
      ),
    );
  }
}
```

---

## Add Annotations

Use `mapboxMap.annotations` to create managers for point, circle, polyline, and polygon annotations. Managers are long-lived — create them once and reuse for updates.

### Point annotations with a custom image

```dart
import 'package:flutter/services.dart' show rootBundle;

PointAnnotationManager? pointAnnotationManager;

Future<void> _addMarkers(MapboxMap mapboxMap) async {
  pointAnnotationManager = await mapboxMap.annotations.createPointAnnotationManager();

  final bytes = await rootBundle.load('assets/marker.png');
  final imageBytes = bytes.buffer.asUint8List();

  final options = <PointAnnotationOptions>[
    PointAnnotationOptions(
      geometry: Point(coordinates: Position(-122.4194, 37.7749)),
      image: imageBytes,
      iconSize: 1.2,
    ),
    PointAnnotationOptions(
      geometry: Point(coordinates: Position(-122.4094, 37.7849)),
      image: imageBytes,
    ),
  ];

  await pointAnnotationManager!.createMulti(options);
}
```

Remember to register the asset in `pubspec.yaml`:

```yaml
flutter:
  assets:
    - assets/marker.png
```

### Tap handling

Use `manager.tapEvents` — this is the current API. `addOnPointAnnotationClickListener` is deprecated.

`tapEvents` returns a `Cancelable` that you store and invoke `.cancel()` on when the listener is no longer needed:

```dart
final Cancelable tapSubscription = pointAnnotationManager!.tapEvents(
  onTap: (annotation) {
    debugPrint('Tapped annotation ${annotation.id}');
  },
);

@override
void dispose() {
  tapSubscription.cancel();
  super.dispose();
}
```

The same pattern — returning a `Cancelable` — exists on every manager's `longPressEvents` and `dragEvents`, and across the other annotation types (`CircleAnnotationManager.tapEvents`, etc.).

### Load annotations from GeoJSON

```dart
import 'dart:convert';
import 'package:flutter/services.dart' show rootBundle;

Future<void> _loadGeoJson(MapboxMap mapboxMap) async {
  final raw = await rootBundle.loadString('assets/coffee_shops.geojson');
  final geo = jsonDecode(raw) as Map<String, dynamic>;
  final features = (geo['features'] as List).cast<Map<String, dynamic>>();

  final manager = await mapboxMap.annotations.createPointAnnotationManager();
  final icon = (await rootBundle.load('assets/coffee.png')).buffer.asUint8List();

  final options = features.map((feature) {
    final coords = feature['geometry']['coordinates'] as List;
    return PointAnnotationOptions(
      geometry: Point(coordinates: Position(coords[0] as double, coords[1] as double)),
      image: icon,
    );
  }).toList();

  await manager.createMulti(options);
}
```

For thousands of features use a style layer (`GeoJsonSource` + `SymbolLayer`) instead of annotations.

---

## Show User Location

Permissions must already be granted (use `permission_handler` or similar) before enabling the puck.

```dart
await mapboxMap.location.updateSettings(LocationComponentSettings(
  enabled: true,
  puckBearingEnabled: true,
  locationPuck: LocationPuck(
    locationPuck2D: DefaultLocationPuck2D(),
  ),
));
```

---

## Camera Control

```dart
// Instant jump
await mapboxMap.setCamera(CameraOptions(
  center: Point(coordinates: Position(-80.1263, 25.7845)),
  zoom: 14,
));

// Animated fly-to
await mapboxMap.flyTo(
  CameraOptions(
    center: Point(coordinates: Position(-80.1263, 25.7845)),
    zoom: 17,
    bearing: 180,
    pitch: 30,
  ),
  MapAnimationOptions(duration: 2000),
);
```

---

## Troubleshooting

### iOS build fails with "platform is lower than deployment target"

The Flutter default iOS deployment target is lower than Mapbox's minimum (iOS 14). Set **Minimum Deployments → iOS** to `14.0` on the Runner target in Xcode. If the project has an `ios/Podfile`, also set `platform :ios, '14.0'` there and re-run `pod install`.

### `setAccessToken` not called

If you forget to call `MapboxOptions.setAccessToken` before creating a `MapWidget`, the map will load with a blank grid. Always call it in `main()` before `runApp`.

### Annotation tap handler not firing

Make sure you're using `manager.tapEvents(onTap: ...)` — `addOnPointAnnotationClickListener` is deprecated. Also confirm the `MapboxMap` controller is captured via `onMapCreated` before you create the annotation manager.

### Hot reload after permissions change

iOS/Android will not re-read manifests or Info.plist on hot reload. Fully restart the app after editing permissions.

---

## Reference Files

- **`references/annotations.md`** — Circle, Polyline, Polygon patterns and GeoJSON source/layer recipes.
- **`references/platform-setup.md`** — Deeper iOS/Android setup, token strategies, release signing notes.

---

## Additional Resources

- [Flutter Maps Guides](https://docs.mapbox.com/flutter/maps/guides/)
- [Markers and Annotations guide](https://docs.mapbox.com/flutter/maps/guides/markers-and-annotations/)
- [User Location guide](https://docs.mapbox.com/flutter/maps/guides/user-location/)
- [Example App](https://github.com/mapbox/mapbox-maps-flutter/tree/main/example)

<!-- chapter:end slug=mapbox-flutter-patterns -->

---

<!-- chapter:begin slug=mapbox-geospatial-operations position=5 -->

## 5. mapbox-geospatial-operations

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-geospatial-operations/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-geospatial-operations/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-geospatial-operations.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (1), referenced from this skill's directory:
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-geospatial-operations/evals/evals.json

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-geospatial-operations
description: Expert guidance on choosing the right geospatial tool based on problem type, accuracy requirements, and performance needs
---

# Mapbox Geospatial Operations Skill

Expert guidance for AI assistants on choosing the right geospatial tools from the Mapbox MCP Server. Focuses on selecting tools based on **what the problem requires** - geometric calculations vs routing, straight-line vs road network, and accuracy needs.

## Core Principle: Problem Type Determines Tool Choice

The Mapbox MCP Server provides two categories of geospatial tools:

1. **Offline Geometric Tools** - Use Turf.js for pure geometric/spatial calculations
2. **Routing & Navigation APIs** - Use Mapbox APIs when you need real-world routing, traffic, or travel times

**The key question: What does the problem actually require?**

### Decision Framework

| Problem Characteristic                                 | Tool Category     | Why                                      |
| ------------------------------------------------------ | ----------------- | ---------------------------------------- |
| **Straight-line distance** (as the crow flies)         | Offline geometric | Accurate for geometric distance          |
| **Road/path distance** (as the crow drives)            | Routing API       | Only routing APIs know road networks     |
| **Travel time**                                        | Routing API       | Requires routing with speed/traffic data |
| **Point containment** (is X inside Y?)                 | Offline geometric | Pure geometric operation                 |
| **Geographic shapes** (buffers, centroids, areas)      | Offline geometric | Mathematical/geometric operations        |
| **Traffic-aware routing**                              | Routing API       | Requires real-time traffic data          |
| **Route optimization** (best order to visit)           | Routing API       | Complex routing algorithm                |
| **High-frequency checks** (e.g., real-time geofencing) | Offline geometric | Instant response, no latency             |

## Decision Matrices by Use Case

### Distance Calculations

**User asks: "How far is X from Y?"**

| What They Actually Mean                            | Tool Choice                         | Why                                      |
| -------------------------------------------------- | ----------------------------------- | ---------------------------------------- |
| Straight-line distance (as the crow flies)         | `distance_tool`                     | Accurate for geometric distance, instant |
| Driving distance (as the crow drives)              | `directions_tool`                   | Only routing knows actual road distance  |
| Walking/cycling distance (as the crow walks/bikes) | `directions_tool`                   | Need specific path network               |
| Travel time                                        | `directions_tool` or `matrix_tool`  | Requires routing with speed data         |
| Distance with current traffic                      | `directions_tool` (driving-traffic) | Need real-time traffic consideration     |

**Example: "What's the distance between these 5 warehouses?"**

- As the crow flies → `distance_tool` (10 calculations, instant)
- As the crow drives → `matrix_tool` (5×5 matrix, one API call, returns actual route distances)

**Key insight:** Use the tool that matches what "distance" means in context. Always clarify: crow flies or crow drives?

### Proximity and Containment

**User asks: "Which points are near/inside this area?"**

| Query Type                   | Tool Choice                                           | Why                                                           |
| ---------------------------- | ----------------------------------------------------- | ------------------------------------------------------------- |
| "Within X meters radius"     | `distance_tool` + filter                              | Simple geometric radius                                       |
| "Within X minutes drive"     | `isochrone_tool` → `point_in_polygon_tool`            | Need routing for travel-time zone, then geometric containment |
| "Inside this polygon"        | `point_in_polygon_tool`                               | Pure geometric containment test                               |
| "Reachable by car in 30 min" | `isochrone_tool`                                      | Requires routing + traffic                                    |
| "Nearest to this point"      | `distance_tool` (geometric) or `matrix_tool` (routed) | Depends on definition of "nearest"                            |

**Example: "Are these 200 addresses in our 30-minute delivery zone?"**

1. Create zone → `isochrone_tool` (routing API - need travel time)
2. Check addresses → `point_in_polygon_tool` (geometric - 200 instant checks)

**Key insight:** Routing for creating travel-time zones, geometric for containment checks

### Routing and Navigation

**User asks: "What's the best route?"**

| Scenario                            | Tool Choice                         | Why                               |
| ----------------------------------- | ----------------------------------- | --------------------------------- |
| A to B directions                   | `directions_tool`                   | Turn-by-turn routing              |
| Optimal order for multiple stops    | `optimization_tool`                 | Solves traveling salesman problem |
| Clean GPS trace                     | `map_matching_tool`                 | Snaps to road network             |
| Just need bearing/compass direction | `bearing_tool`                      | Simple geometric calculation      |
| Route with traffic                  | `directions_tool` (driving-traffic) | Real-time traffic awareness       |
| Fixed-order waypoints               | `directions_tool` with waypoints    | Routing through specific points   |

**Example: "Navigate from hotel to airport"**

- Need turn-by-turn → `directions_tool`
- Just need to know "it's northeast" → `bearing_tool`

**Key insight:** Routing tools for actual navigation, geometric tools for directional info

### Area and Shape Operations

**User asks: "Create a zone around this location"**

| Requirement               | Tool Choice      | Why                      |
| ------------------------- | ---------------- | ------------------------ |
| Simple circular buffer    | `buffer_tool`    | Geometric circle/radius  |
| Travel-time zone          | `isochrone_tool` | Based on routing network |
| Calculate area size       | `area_tool`      | Geometric calculation    |
| Simplify complex boundary | `simplify_tool`  | Geometric simplification |
| Find center of shape      | `centroid_tool`  | Geometric centroid       |

**Example: "Show 5km coverage around each store"**

- 5km radius → `buffer_tool` (geometric circles)
- "What customers can reach in 15 min?" → `isochrone_tool` (routing-based)

**Key insight:** Geometric tools for distance-based zones, routing tools for time-based zones

## Performance and Scale Considerations

### When Volume Affects Tool Choice

**Small operations (< 100 calculations):**

- Geometric tools: Instant, iterate freely
- Routing APIs: Fast enough for most uses

**Medium operations (100-1,000 calculations):**

- Geometric tools: Still fast, no concerns
- Routing APIs: Consider batch operations (matrix_tool)

**Large operations (> 1,000 calculations):**

- Geometric tools: May need optimization but still fast
- Routing APIs: Definitely use batch tools (matrix_tool handles up to 25×25)

**Key insight:** Volume rarely affects geometric tool choice, but routing APIs have batch tools for efficiency

### Real-Time vs Batch

| Use Case                            | Approach             | Tool Choice                              |
| ----------------------------------- | -------------------- | ---------------------------------------- |
| Real-time geofencing (every second) | Geometric checks     | `point_in_polygon_tool` (instant)        |
| Route planning (one-time)           | Full routing         | `directions_tool` or `optimization_tool` |
| Periodic proximity checks           | Geometric distance   | `distance_tool`                          |
| Live traffic routing                | Routing with traffic | `directions_tool` (driving-traffic)      |

**Architecture note for high-frequency geofencing:** If the application calls containment checks at very high rates (e.g., 50 vehicles every 2 seconds = 25 checks/second), calling MCP tools over the network adds agent-reasoning overhead that makes it impractical. In those cases, recommend using Turf.js directly in-process (`turf.booleanPointInPolygon`) for the hot path, and reserve MCP tools for peripheral tasks like zone definition (`isochrone_tool`), rerouting (`directions_tool`), or visualization (`static_map_image_tool`).

## Common Scenarios and Optimal Approaches

### Scenario 1: Store Locator

**User: "Find the closest store and show 5km coverage"**

**Optimal approach:**

1. Search stores → `category_search_tool` (returns distances automatically)
2. Create coverage zone → `buffer_tool` (5km geometric circle)
3. Visualize → `static_map_image_tool`

**Why:** Search already gives distances; geometric buffer for simple radius

### Scenario 2: Delivery Route Optimization

**User: "Optimize delivery to 8 addresses / stops"**

**Optimal approach:**

1. **Geocode addresses (if needed)** → Use `search_and_geocode_tool` to convert any street addresses to coordinates. Even when coordinates are already provided, mention this as an optional pre-step — real-world delivery lists often contain a mix of addresses and coordinates.
2. **Optimize route** → `optimization_tool` (TSP solver — reorders stops to minimize total drive time)

**Why `optimization_tool` and NOT these alternatives:**

- **`directions_tool`** only routes A → B (or through fixed-order waypoints). It does NOT reorder stops — if you pass 8 stops, it routes them in the order given, which is almost never optimal.
- **`matrix_tool`** gives travel times between all pairs of stops (8×8 = 64 values), but it does NOT compute the optimal ordering. You'd need to solve TSP yourself on top of the matrix — `optimization_tool` does this for you in one call.

Always mention `search_and_geocode_tool` as a useful companion for geocoding delivery addresses before optimization.

### Scenario 3: Service Area Validation

**User: "Which of these 200 addresses can we deliver to in 30 minutes?"**

**Optimal approach:**

1. Create delivery zone → `isochrone_tool` (30-minute driving)
2. Check each address → `point_in_polygon_tool` (200 geometric checks)

**Why:** Routing for accurate travel-time zone, geometric for fast containment checks

### Scenario 4: GPS Trace Analysis

**User: "How long was this bike ride?"**

**Optimal approach:**

1. Clean GPS trace → `map_matching_tool` (snap to bike paths)
2. Get distance → Use API response or calculate with `distance_tool`

**Why:** Need road/path matching; distance calculation either way works

### Scenario 5: Coverage Analysis

**User: "What's our total service area?"**

**Optimal approach:**

1. Create buffers around each location → `buffer_tool`
2. Calculate total area → `area_tool`
3. Or, if time-based → `isochrone_tool` for each location

**Why:** Geometric for distance-based coverage, routing for time-based

## Anti-Patterns: Using the Wrong Tool Type

### ❌ Don't: Use geometric tools for routing questions

```javascript
// WRONG: User asks "how long to drive there?"
distance_tool({ from: A, to: B });
// Returns 10km as the crow flies, but actual drive is 15km

// CORRECT: Need routing for driving distance
directions_tool({
  coordinates: [
    { longitude: A[0], latitude: A[1] },
    { longitude: B[0], latitude: B[1] }
  ],
  routing_profile: 'mapbox/driving'
});
// Returns actual road distance and drive time as the crow drives
```

**Why wrong:** As the crow flies ≠ as the crow drives

### ❌ Don't: Use routing APIs for geometric operations

```javascript
// WRONG: Check if point is in polygon
// (Can't do this with routing APIs)

// CORRECT: Pure geometric operation
point_in_polygon_tool({ point: location, polygon: boundary });
```

**Why wrong:** Routing APIs don't do geometric containment

### ❌ Don't: Confuse "near" with "reachable"

```javascript
// User asks: "What's reachable in 20 minutes?"

// WRONG: 20-minute distance at average speed
distance_tool + calculate 20min * avg_speed

// CORRECT: Actual routing with road network
isochrone_tool({
  coordinates: {longitude: startLng, latitude: startLat},
  contours_minutes: [20],
  profile: "mapbox/driving"
})
```

**Why wrong:** Roads aren't straight lines; traffic varies

### ❌ Don't: Use routing when bearing is sufficient

```javascript
// User asks: "Which direction is the airport?"

// OVERCOMPLICATED: Full routing
directions_tool({
  coordinates: [
    { longitude: hotel[0], latitude: hotel[1] },
    { longitude: airport[0], latitude: airport[1] }
  ]
});

// BETTER: Just need bearing
bearing_tool({ from: hotel, to: airport });
// Returns: "Northeast (45°)"
```

**Why better:** Simpler, instant, answers the actual question

## Hybrid Approaches: Combining Tool Types

Some problems benefit from using both geometric and routing tools:

### Pattern 1: Routing + Geometric Filter

```
1. directions_tool → Get route geometry
2. buffer_tool → Create corridor around route
3. category_search_tool → Find POIs in corridor
4. point_in_polygon_tool → Filter to those actually along route
```

**Use case:** "Find gas stations along my route"

### Pattern 2: Routing + Distance Calculation

```
1. category_search_tool → Find 10 nearby locations
2. distance_tool → Calculate straight-line distances (geometric)
3. For top 3, use directions_tool → Get actual driving time
```

**Use case:** Quickly narrow down, then get precise routing for finalists

### Pattern 3: Isochrone + Containment

```
1. isochrone_tool → Create travel-time zone (routing)
2. point_in_polygon_tool → Check hundreds of addresses (geometric)
```

**Use case:** "Which customers are in our delivery zone?"

## Decision Algorithm

When user asks a geospatial question:

```
1. Does it require routing, roads, or travel times?
   YES → Use routing API (directions, matrix, isochrone, optimization)
   NO → Continue

2. Does it require traffic awareness?
   YES → Use directions_tool or isochrone_tool with traffic profile
   NO → Continue

3. Is it a geometric/spatial operation?
   - Distance between points (straight-line) → distance_tool
   - Point containment → point_in_polygon_tool
   - Area calculation → area_tool
   - Buffer/zone → buffer_tool
   - Direction/bearing → bearing_tool
   - Geometric center → centroid_tool
   - Bounding box → bounding_box_tool
   - Simplification → simplify_tool

4. Is it a search/discovery operation?
   YES → Use search tools (search_and_geocode, category_search)
```

## Key Decision Questions

Before choosing a tool, ask:

1. **Does "distance" mean as the crow flies or as the crow drives?**
   - As the crow flies (straight-line) → geometric tools
   - As the crow drives (road distance) → routing APIs

2. **Does the user need travel time?**
   - Yes → routing APIs (only they know speeds/traffic)
   - No → geometric tools may suffice

3. **Is this about roads/paths or pure spatial relationships?**
   - Roads/paths → routing APIs
   - Spatial relationships → geometric tools

4. **Does this need to happen in real-time with low latency?**
   - Yes + geometric problem → offline tools (instant)
   - Yes + routing problem → use routing APIs (still fast)

5. **Is accuracy critical, or is approximation OK?**
   - Critical + routing → routing APIs
   - Approximation OK → geometric tools may work

## Terminology Guide

Understanding what users mean:

| User Says             | Usually Means                                      | Tool Type   |
| --------------------- | -------------------------------------------------- | ----------- |
| "Distance"            | Context-dependent! Ask: crow flies or crow drives? | Varies      |
| "How far"             | Often as the crow drives (road distance)           | Routing API |
| "Nearby"              | Usually as the crow flies (straight-line radius)   | Geometric   |
| "Close"               | Could be either - clarify!                         | Ask         |
| "Reachable"           | Travel-time based (crow drives with traffic)       | Routing API |
| "Inside/contains"     | Geometric containment                              | Geometric   |
| "Navigate/directions" | Turn-by-turn routing                               | Routing API |
| "Bearing/direction"   | Compass direction (crow flies)                     | Geometric   |

## Quick Reference

### Geometric Operations (Offline Tools)

- `distance_tool` - Straight-line distance between two points
- `bearing_tool` - Compass direction from A to B
- `midpoint_tool` - Midpoint between two points
- `point_in_polygon_tool` - Is point inside polygon?
- `area_tool` - Calculate polygon area
- `buffer_tool` - Create circular buffer/zone
- `centroid_tool` - Geometric center of polygon
- `bbox_tool` - Min/max coordinates of geometry
- `simplify_tool` - Reduce geometry complexity

### Routing & Navigation (APIs)

- `directions_tool` - Turn-by-turn routing
- `matrix_tool` - Many-to-many travel times
- `optimization_tool` - Route optimization (TSP)
- `isochrone_tool` - Travel-time zones
- `map_matching_tool` - Snap GPS to roads

### When to Use Each Category

**Use Geometric Tools When:**

- Problem is spatial/mathematical (containment, area, bearing)
- Straight-line distance is appropriate
- Need instant results for real-time checks
- Pure geometry (no roads/traffic involved)

**Use Routing APIs When:**

- Need actual driving/walking/cycling distances
- Need travel times
- Need to consider road networks
- Need traffic awareness
- Need route optimization
- Need turn-by-turn directions

## REST API honesty (Directions / Isochrone)

When generating browser demos against Mapbox REST APIs (not only MCP tools):

### Directions — use the route object metrics

```javascript
const route = data.routes[0];
// GOOD — meters / seconds from the API
stats.textContent =
  `Distance ${(route.distance / 1000).toFixed(2)} km · ` + `Duration ${Math.round(route.duration / 60)} min`;

// BAD — cosmetic labels or hardcoded ETAs that look like a working demo
// "Len 0.00 km · Time 2 min" / Math.round(120/60)
```

### Isochrone — request multiple contours when the prompt asks for bands

```javascript
// GOOD — three travel-time bands
`contours_minutes=15,30,60`;

// BAD — a single contour when the UX needs 15 / 30 / 60
`contours_minutes=30`;
```

## Integration with Other Skills

**Works with:**

- **mapbox-search-patterns**: Search for locations, then use geospatial operations
- **mapbox-web-performance-patterns**: Optimize rendering of geometric calculations
- **mapbox-token-security**: Ensure requests use properly scoped tokens

## Resources

- [Mapbox MCP Server](https://github.com/mapbox/mcp-server)
- [Turf.js Documentation](https://turfjs.org/) (Powers geometric tools)
- [Mapbox Directions API](https://docs.mapbox.com/api/navigation/directions/)
- [Mapbox Isochrone API](https://docs.mapbox.com/api/navigation/isochrone/)
- [Mapbox Matrix API](https://docs.mapbox.com/api/navigation/matrix/)
- [Mapbox Optimization API](https://docs.mapbox.com/api/navigation/optimization/)

<!-- chapter:end slug=mapbox-geospatial-operations -->

---

<!-- chapter:begin slug=mapbox-google-maps-migration position=6 -->

## 6. mapbox-google-maps-migration

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-google-maps-migration/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-google-maps-migration.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (7), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/evals/evals.json
  - `references/api-services.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/references/api-services.md
  - `references/clustering-styling.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/references/clustering-styling.md
  - `references/data-performance.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/references/data-performance.md
  - `references/directions-controls.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/references/directions-controls.md
  - `references/shapes-geocoding.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-google-maps-migration/references/shapes-geocoding.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-google-maps-migration
description: Migration guide for developers moving from Google Maps Platform to Mapbox GL JS, covering API equivalents, pattern translations, and key differences
---

# Mapbox Google Maps Migration Skill

Comprehensive guidance for migrating from Google Maps Platform to Mapbox GL JS. Provides API equivalents, pattern translations, and strategies for successful migration.

## Core Philosophy Differences

### Google Maps: Imperative & Object-Oriented

- Create objects (Marker, Polygon, etc.)
- Add to map with `.setMap(map)`
- Update properties with setters
- Heavy reliance on object instances

### Mapbox GL JS: Declarative & Data-Driven

- Add data sources
- Define layers (visual representation)
- Style with JSON
- Update data, not object properties

**Key Insight:** Mapbox treats everything as data + styling, not individual objects.

## Map Initialization

### Google Maps

```javascript
const map = new google.maps.Map(document.getElementById('map'), {
  center: { lat: 37.7749, lng: -122.4194 },
  zoom: 12,
  mapTypeId: 'roadmap' // or 'satellite', 'hybrid', 'terrain'
});
```

### Mapbox GL JS

```javascript
mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';
const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/streets-v12', // or satellite-v9, outdoors-v12
  center: [-122.4194, 37.7749], // [lng, lat] - note the order!
  zoom: 12
});
```

**Key Differences:**

- **Coordinate order:** Google uses `{lat, lng}`, Mapbox uses `[lng, lat]`
- **Authentication:** Google uses API key in script tag, Mapbox uses access token in code
- **Styling:** Google uses map types, Mapbox uses full style URLs

## API Equivalents Reference

### Map Methods

| Google Maps              | Mapbox GL JS                           | Notes                         |
| ------------------------ | -------------------------------------- | ----------------------------- |
| `map.setCenter(latLng)`  | `map.setCenter([lng, lat])`            | Coordinate order reversed     |
| `map.getCenter()`        | `map.getCenter()`                      | Returns LngLat object         |
| `map.setZoom(zoom)`      | `map.setZoom(zoom)`                    | Same behavior                 |
| `map.getZoom()`          | `map.getZoom()`                        | Same behavior                 |
| `map.panTo(latLng)`      | `map.panTo([lng, lat])`                | Animated pan                  |
| `map.fitBounds(bounds)`  | `map.fitBounds([[lng,lat],[lng,lat]])` | Different bound format        |
| `map.setMapTypeId(type)` | `map.setStyle(styleUrl)`               | Completely different approach |
| `map.getBounds()`        | `map.getBounds()`                      | Similar                       |

### Map Events

| Google Maps                                       | Mapbox GL JS           | Notes                 |
| ------------------------------------------------- | ---------------------- | --------------------- |
| `google.maps.event.addListener(map, 'click', fn)` | `map.on('click', fn)`  | Simpler syntax        |
| `event.latLng`                                    | `event.lngLat`         | Event property name   |
| `'center_changed'`                                | `'move'` / `'moveend'` | Different event names |
| `'zoom_changed'`                                  | `'zoom'` / `'zoomend'` | Different event names |
| `'bounds_changed'`                                | `'moveend'`            | No direct equivalent  |
| `'mousemove'`                                     | `'mousemove'`          | Same                  |
| `'mouseout'`                                      | `'mouseleave'`         | Different name        |

## Markers and Points

### Simple Marker

**Google Maps:**

```javascript
const marker = new google.maps.Marker({
  position: { lat: 37.7749, lng: -122.4194 },
  map: map,
  title: 'San Francisco',
  icon: 'custom-icon.png'
});

// Remove marker
marker.setMap(null);
```

**Mapbox GL JS:**

```javascript
// Create marker
const marker = new mapboxgl.Marker()
  .setLngLat([-122.4194, 37.7749])
  .setPopup(new mapboxgl.Popup().setText('San Francisco'))
  .addTo(map);

// Remove marker
marker.remove();
```

### Multiple Markers

**Google Maps:**

```javascript
const markers = locations.map(
  (loc) =>
    new google.maps.Marker({
      position: { lat: loc.lat, lng: loc.lng },
      map: map
    })
);
```

**Mapbox GL JS (Equivalent Approach):**

```javascript
// Same object-oriented approach
const markers = locations.map((loc) => new mapboxgl.Marker().setLngLat([loc.lng, loc.lat]).addTo(map));
```

**Mapbox GL JS (Data-Driven Approach - Recommended for 100+ points):**

```javascript
// Add as GeoJSON source + layer (uses WebGL, not DOM)
map.addSource('points', {
  type: 'geojson',
  data: {
    type: 'FeatureCollection',
    features: locations.map((loc) => ({
      type: 'Feature',
      geometry: { type: 'Point', coordinates: [loc.lng, loc.lat] },
      properties: { name: loc.name }
    }))
  }
});

map.addLayer({
  id: 'points-layer',
  type: 'circle', // or 'symbol' for icons
  source: 'points',
  paint: {
    'circle-radius': 8,
    'circle-color': '#ff0000'
  }
});
```

**Performance Advantage:** Google Maps renders all markers as DOM elements (even when using the Data Layer), which becomes slow with 500+ markers. Mapbox's circle and symbol layers are rendered by WebGL, making them much faster for large datasets (1,000-10,000+ points). This is a significant advantage when building applications with many points.

## Info Windows / Popups

### Google Maps

```javascript
const infowindow = new google.maps.InfoWindow({
  content: '<h3>Title</h3><p>Content</p>'
});

marker.addListener('click', () => {
  infowindow.open(map, marker);
});
```

### Mapbox GL JS

```javascript
// Option 1: Attach to marker
const marker = new mapboxgl.Marker()
  .setLngLat([-122.4194, 37.7749])
  .setPopup(new mapboxgl.Popup().setHTML('<h3>Title</h3><p>Content</p>'))
  .addTo(map);

// Option 2: On layer click (for data-driven markers)
map.on('click', 'points-layer', (e) => {
  const coordinates = e.features[0].geometry.coordinates.slice();
  const description = e.features[0].properties.description;

  new mapboxgl.Popup().setLngLat(coordinates).setHTML(description).addTo(map);
});
```

## Migration Strategy

### Step 1: Audit Current Implementation

Identify all Google Maps features you use:

- [ ] Basic map with markers
- [ ] Info windows/popups
- [ ] Polygons/polylines
- [ ] Geocoding
- [ ] Directions
- [ ] Clustering
- [ ] Custom styling
- [ ] Drawing tools
- [ ] Street View (no Mapbox equivalent)
- [ ] Other advanced features

### Step 2: Set Up Mapbox

```html
<!-- Replace Google Maps script -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.18.1/mapbox-gl.css" rel="stylesheet" />
```

### Step 3: Convert Core Map

Start with basic map initialization:

1. Replace `new google.maps.Map()` with `new mapboxgl.Map()`
2. Fix coordinate order (lat,lng -> lng,lat)
3. Update zoom/center

### Step 4: Convert Features One by One

Prioritize by complexity:

1. **Easy:** Map controls, basic markers
2. **Medium:** Popups, polygons, lines
3. **Complex:** Clustering, custom styling, data updates

### Step 5: Update Event Handlers

Change event syntax:

- `google.maps.event.addListener()` -> `map.on()`
- Update event property names (`latLng` -> `lngLat`)

### Step 6: Optimize for Mapbox

Take advantage of Mapbox features:

- Convert multiple markers to data-driven layers
- Use clustering (built-in)
- Leverage vector tiles for custom styling
- Use expressions for dynamic styling

### Step 7: Test Thoroughly

- Cross-browser testing
- Mobile responsiveness
- Performance with real data volumes
- Touch/gesture interactions

## Gotchas and Common Issues

### Coordinate Order

```javascript
// Google Maps
{ lat: 37.7749, lng: -122.4194 }

// Mapbox (REVERSED!)
[-122.4194, 37.7749]
```

**Always double-check coordinate order!**

### Event Properties

```javascript
// Google Maps
map.on('click', (e) => {
  console.log(e.latLng.lat(), e.latLng.lng());
});

// Mapbox
map.on('click', (e) => {
  console.log(e.lngLat.lat, e.lngLat.lng);
});
```

### Timing Issues

```javascript
// Google Maps - immediate
const marker = new google.maps.Marker({ map: map });

// Mapbox - wait for load
map.on('load', () => {
  map.addSource(...);
  map.addLayer(...);
});
```

### Removing Features

```javascript
// Google Maps
marker.setMap(null);

// Mapbox - must remove both
map.removeLayer('layer-id');
map.removeSource('source-id');
```

### Updating Data Without Flash

**Never** remove and re-add layers to update data — this reinitializes WebGL resources and causes a visible flash. Instead:

```javascript
// ✅ Update data in place (no flash)
map.getSource('stores').setData(newGeoJSON);

// ✅ Filter existing data (GPU-side, fastest)
map.setFilter('stores-layer', ['==', ['get', 'category'], 'coffee']);

// ❌ BAD: remove + re-add causes flash
map.removeLayer('stores-layer');
map.removeSource('stores');
map.addSource('stores', { ... });
map.addLayer({ ... });
```

## When NOT to Migrate

Consider staying with Google Maps if:

- **Street View is critical** - Mapbox doesn't have equivalent
- **Tight Google Workspace integration** - Places API deeply integrated
- **Already heavily optimized** - Migration cost > benefits
- **Team expertise** - Retraining costs too high
- **Short-term project** - Not worth migration effort

## Quick Reference: Side-by-Side Comparison

```javascript
// GOOGLE MAPS
const map = new google.maps.Map(el, {
  center: { lat: 37.7749, lng: -122.4194 },
  zoom: 12
});

const marker = new google.maps.Marker({
  position: { lat: 37.7749, lng: -122.4194 },
  map: map
});

google.maps.event.addListener(map, 'click', (e) => {
  console.log(e.latLng.lat(), e.latLng.lng());
});

// MAPBOX GL JS
mapboxgl.accessToken = 'YOUR_TOKEN';
const map = new mapboxgl.Map({
  container: el,
  center: [-122.4194, 37.7749], // REVERSED!
  zoom: 12,
  style: 'mapbox://styles/mapbox/streets-v12'
});

const marker = new mapboxgl.Marker()
  .setLngLat([-122.4194, 37.7749]) // REVERSED!
  .addTo(map);

map.on('click', (e) => {
  console.log(e.lngLat.lat, e.lngLat.lng);
});
```

**Remember:** lng, lat order in Mapbox!

## Additional Resources

- [Mapbox GL JS Documentation](https://docs.mapbox.com/mapbox-gl-js/)
- [Official Google Maps to Mapbox Migration Guide](https://docs.mapbox.com/help/tutorials/google-to-mapbox/)
- [Mapbox Examples](https://docs.mapbox.com/mapbox-gl-js/examples/)
- [Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/)

## Integration with Other Skills

**Works with:**

- **mapbox-web-integration-patterns**: Framework-specific migration guidance
- **mapbox-web-performance-patterns**: Optimize after migration
- **mapbox-token-security**: Secure your Mapbox tokens properly
- **mapbox-geospatial-operations**: Use Mapbox's geospatial tools effectively
- **mapbox-search-patterns**: Migrate geocoding/search functionality

## Reference Files

The following reference files contain detailed migration guides for specific topics. Load them when working on those areas:

- **`references/shapes-geocoding.md`** — Polygons, Polylines, Custom Icons, Geocoding
- **`references/directions-controls.md`** — Directions/Routing, Controls
- **`references/clustering-styling.md`** — Clustering, Styling/Appearance
- **`references/data-performance.md`** — Data Updates, Performance, Common Migration Patterns (Store Locator, Drawing Tools, Heatmaps)
- **`references/api-services.md`** — API Services Comparison, Pricing, Plugins, Framework Integration, Testing, Migration Checklist

To load a reference, read the file relative to this skill directory, e.g.:

```
Load references/shapes-geocoding.md
```

<!-- chapter:end slug=mapbox-google-maps-migration -->

---

<!-- chapter:begin slug=mapbox-ios-patterns position=7 -->

## 7. mapbox-ios-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-ios-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-ios-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (7), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/evals/evals.json
  - `references/annotations.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/references/annotations.md
  - `references/camera-styles.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/references/camera-styles.md
  - `references/custom-data.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/references/custom-data.md
  - `references/interactions.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/references/interactions.md
  - `references/location-tracking.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-ios-patterns/references/location-tracking.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-ios-patterns
description: Official integration patterns for Mapbox Maps SDK on iOS. Covers installation, adding markers, user location, custom data, styles, camera control, and featureset interactions. Based on official Mapbox documentation.
---

# Mapbox iOS Integration Patterns

Official patterns for integrating Mapbox Maps SDK v11 on iOS with Swift, SwiftUI, and UIKit.

**Use this skill when:**

- Installing and configuring Mapbox Maps SDK for iOS
- Adding markers and annotations to maps
- Showing user location and tracking with camera
- Adding custom data (GeoJSON) to maps
- Working with map styles, camera, or user interaction
- Handling feature interactions and taps

**Official Resources:**

- [iOS Maps Guides](https://docs.mapbox.com/ios/maps/guides/)
- [API Reference](https://docs.mapbox.com/ios/maps/api-reference/)
- [Example Apps](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples)

---

## Installation & Setup

### Requirements

- iOS 14+
- Xcode 15+
- Swift 5.9+
- Free Mapbox account

### Step 1: Configure Access Token

Add your public token to `Info.plist`:

```xml
<key>MBXAccessToken</key>
<string>pk.your_mapbox_token_here</string>
```

**Get your token:** Sign in at [mapbox.com](https://account.mapbox.com/access-tokens/)

### Step 2: Add Swift Package Dependency

1. **File → Add Package Dependencies**
2. **Enter URL:** `https://github.com/mapbox/mapbox-maps-ios.git`
3. **Version:** "Up to Next Major" from `11.0.0`
4. **Verify** four dependencies appear: MapboxCommon, MapboxCoreMaps, MapboxMaps, Turf

**Alternative:** CocoaPods or direct download ([install guide](https://docs.mapbox.com/ios/maps/guides/install/))

---

## Map Initialization

### SwiftUI Pattern

**Basic map:**

```swift
import SwiftUI
import MapboxMaps

struct ContentView: View {
    @State private var viewport: Viewport = .camera(
        center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
        zoom: 12
    )

    var body: some View {
        Map(viewport: $viewport)
            .mapStyle(.standard)
    }
}
```

**With ornaments:**

```swift
Map(viewport: $viewport)
    .mapStyle(.standard)
    .ornamentOptions(OrnamentOptions(
        scaleBar: .init(visibility: .visible),
        compass: .init(visibility: .adaptive),
        logo: .init(position: .bottomLeading)
    ))
```

### UIKit Pattern

```swift
import UIKit
import MapboxMaps

class MapViewController: UIViewController {
    private var mapView: MapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let options = MapInitOptions(
            cameraOptions: CameraOptions(
                center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
                zoom: 12
            )
        )

        mapView = MapView(frame: view.bounds, mapInitOptions: options)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(mapView)

        mapView.mapboxMap.loadStyle(.standard)
    }
}
```

---

## Add Markers

The SDK offers three ways to place a point on the map. Pick the simplest one that fits.

**Agent note:** A SwiftUI Mapbox sketch is incomplete without at least one annotation (`Marker`, `PointAnnotation`, or `MapViewAnnotation`). Do not ship a bare `Map { }` with no pin.

### Which API should I use?

| API                                                       | Use it when                                                                                    | Platforms       | Notes                                                                                                                                                                        |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Marker` (Markers API)                                    | You need a default pin and don't have a custom image asset                                     | SwiftUI only    | No image assets required. Experimental SPI — needs `@_spi(Experimental) import MapboxMaps`. Best < 100 markers.                                                              |
| `PointAnnotation`                                         | You have a custom image and want layer-level placement                                         | SwiftUI + UIKit | Backed by a symbol layer, so it scales well to hundreds of markers. Accepts any `UIImage` that `UIKit` can render.                                                           |
| View annotations (`ViewAnnotation` / `MapViewAnnotation`) | You want to render a full native view (card, badge, animated content) anchored to a coordinate | SwiftUI + UIKit | SwiftUI uses `MapViewAnnotation`; UIKit uses `mapView.viewAnnotations` with a `ViewAnnotation`. Each annotation is a real view — costs more than `PointAnnotation` at scale. |

For hundreds or thousands of features, use a style layer (`SymbolLayer` on a `GeoJSONSource`) instead of annotations.

### Markers API (recommended for simple cases, SwiftUI)

```swift
import SwiftUI
@_spi(Experimental) import MapboxMaps

struct ContentView: View {
    var body: some View {
        Map {
            Marker(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
                .color(.red)
                .text("San Francisco")
        }
    }
}
```

Multiple markers from a collection:

```swift
Map {
    ForEvery(locations, id: \.id) { location in
        Marker(coordinate: location.coordinate)
            .color(.red)
            .text(location.name)
    }
}
```

> **Scaling note.** `Marker` and `PointAnnotation` each create their own view or symbol entry per pin — fine up to about 100 markers. For larger datasets (hundreds or thousands of features — common with open-ended GeoJSON feeds), load the data into a `GeoJSONSource` and render it with a `SymbolLayer` instead. That scales to thousands of features and enables clustering.

### PointAnnotation (custom image)

**SwiftUI:**

```swift
Map(viewport: $viewport) {
    PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
        .image(.init(image: UIImage(named: "marker")!, name: "marker"))
}
```

**UIKit:**

```swift
// Create annotation manager (once, reuse for updates)
var pointAnnotationManager = mapView.annotations.makePointAnnotationManager()

// Create marker
var annotation = PointAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194))
annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
annotation.iconAnchor = .bottom

// Add to map
pointAnnotationManager.annotations = [annotation]
```

**Multiple markers:**

```swift
let annotations = locations.map { coordinate in
    var annotation = PointAnnotation(coordinate: coordinate)
    annotation.image = .init(image: UIImage(named: "marker")!, name: "marker")
    return annotation
}

pointAnnotationManager.annotations = annotations
```

---

## Show User Location

**Step 1: Add location permission to Info.plist:**

```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show your location on the map</string>
```

**Step 2: Request permissions and show location:**

```swift
import CoreLocation

// Request permissions
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()

// Show user location puck
mapView.location.options.puckType = .puck2D()
mapView.location.options.puckBearingEnabled = true
```

---

## Performance Best Practices

### Reuse Annotation Managers

```swift
// ❌ Don't create new managers repeatedly
func updateMarkers() {
    let manager = mapView.annotations.makePointAnnotationManager()
    manager.annotations = markers
}

// ✅ Create once, reuse
let pointAnnotationManager: PointAnnotationManager

init() {
    pointAnnotationManager = mapView.annotations.makePointAnnotationManager()
}

func updateMarkers() {
    pointAnnotationManager.annotations = markers
}
```

### Batch Annotation Updates

```swift
// ✅ Update all at once
pointAnnotationManager.annotations = newAnnotations

// ❌ Don't update one by one
for annotation in newAnnotations {
    pointAnnotationManager.annotations.append(annotation)
}
```

### Memory Management

```swift
// Use weak self in closures
mapView.gestures.onMapTap.observe { [weak self] context in
    self?.handleTap(context.coordinate)
}.store(in: &cancelables)

// Clean up on deinit
deinit {
    cancelables.forEach { $0.cancel() }
}
```

### Use Standard Style

```swift
// ✅ Standard style is optimized and recommended
.mapStyle(.standard)

// Use other styles only when needed for specific use cases
.mapStyle(.standardSatellite) // Satellite imagery
```

---

## Troubleshooting

### Map Not Displaying

**Check:**

1. ✅ `MBXAccessToken` in Info.plist
2. ✅ Token is valid (test at mapbox.com)
3. ✅ MapboxMaps framework imported
4. ✅ MapView added to view hierarchy
5. ✅ Correct frame/constraints set

### Style Not Loading

```swift
mapView.mapboxMap.onStyleLoaded.observe { [weak self] _ in
    print("Style loaded successfully")
    // Add layers and sources here
}.store(in: &cancelables)
```

### Performance Issues

- Use `.standard` style (recommended and optimized)
- Limit visible annotations to viewport
- Reuse annotation managers
- Avoid frequent style reloads
- Batch annotation updates

---

## Reference Files

Load these references when the task requires deeper patterns:

- **`references/annotations.md`** — Circle, Polyline, Polygon Annotations
- **`references/location-tracking.md`** — Camera Follow User + Get Current Location
- **`references/custom-data.md`** — GeoJSON: Lines, Polygons, Points, Update/Remove
- **`references/camera-styles.md`** — Camera Control + Map Styles
- **`references/interactions.md`** — Featureset Interactions, Custom Layer Taps, Long Press, Gestures

---

## Additional Resources

- [iOS Maps Guides](https://docs.mapbox.com/ios/maps/guides/)
- [API Reference](https://docs.mapbox.com/ios/maps/api/11.18.1/documentation/mapboxmaps/)
- [Interactions Guide](https://docs.mapbox.com/ios/maps/guides/user-interaction/Interactions/)
- [SwiftUI User Guide](https://docs.mapbox.com/ios/maps/api/11.18.1/documentation/mapboxmaps/swiftui-user-guide)
- [Example Apps](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples)
- [Migration Guide (v10 → v11)](https://docs.mapbox.com/ios/maps/guides/migrate-to-v11/)

<!-- chapter:end slug=mapbox-ios-patterns -->

---

<!-- chapter:begin slug=mapbox-location-grounding position=8 -->

## 8. mapbox-location-grounding

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-location-grounding/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-location-grounding/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-location-grounding.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (1), referenced from this skill's directory:
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-location-grounding/evals/evals.json

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-location-grounding
description: Compose Mapbox MCP tools to produce grounded, cited location-aware responses from live data instead of training data
---

# Mapbox Location Grounding Skill

Teaches AI assistants how to ground location-aware responses in live Mapbox data by composing MCP tools into a structured, cited answer. Use this instead of relying on training data for place names, POIs, ratings, or travel times — which are stale and prone to hallucination.

## When to Use Grounding

Ground responses when the user asks about:

- "What's near [location]?" or "What's around [coordinate]?"
- "Describe this neighborhood / area"
- "Find [category] within walking/driving distance"
- "What can I do near [address]?"
- "How long does it take to get from A to B?"
- "What's within a 10-minute walk of here?"
- "How far is it between these locations?"
- Real estate, travel, mobility, or local discovery use cases
- Any question where place accuracy, recency, or travel time matters

**Never answer location questions from training data alone.** Always retrieve live data.

## Grounding Tool Composition

### Preferred: single tool call

If `ground_location_tool` is available, use it — it handles reverse geocoding, POI search, place details enrichment, isochrone, and a static map image in one call:

```
ground_location_tool(
  longitude, latitude,
  query: "restaurant",   // optional — category or subcategory of nearby places to find
  profile: "mapbox/walking",    // optional — travel profile for isochrone
  contours_minutes: [5, 10, 15]
)
```

Returns:

- Neighborhood/place name from reverse geocoding
- Nearby POIs with distances, ratings, price levels, and popularity (when available)
- Travel-time reachability from isochrone
- A static map image for visual context
- Citations for all data sources

Do not call `reverse_geocode_tool`, `category_search_tool`, `place_details_tool`, or `isochrone_tool` separately — they are already composed inside this tool.

### Query parameter

The `query` parameter accepts **category or subcategory terms** — not attribute preferences:

- Supported: `"restaurant"`, `"coffee"`, `"park"`, `"Italian restaurant"`, `"EV charging station"`
- Not supported: `"family-friendly"`, `"fast charging"`, `"outdoor seating"` — these are not filterable attributes in Mapbox data

To help users find places matching a preference (e.g. "family-friendly"), search by category (`"restaurant"`) and use the returned rating and price data to inform the recommendation.

### Fallback: manual composition

If `ground_location_tool` is not available, build the grounded response by composing these tools in order:

#### Step 1 — Establish place context

```
reverse_geocode_tool(longitude, latitude, types: "neighborhood,locality,place")
```

Returns: neighborhood, city, region, country. This is the anchor for the response.

#### Step 2 — Retrieve nearby POIs

For specific names or brands:

```
search_and_geocode_tool(query, proximity: {longitude, latitude}, limit: 10)
```

For generic categories:

```
category_search_tool(category, proximity: {longitude, latitude}, limit: 10)
```

#### Step 3 — Enrich POIs with ratings and price (optional but high-value)

For each POI with a `mapbox_id`, call in parallel:

```
place_details_tool(mapbox_id, attribute_sets: ["visit"])
```

Returns: rating, price level, popularity, and opening hours per place.

#### Step 4 — Add travel-time context (optional but high-value)

```
isochrone_tool(
  coordinates: {longitude, latitude},
  profile: "mapbox/walking",    // or "mapbox/driving", "mapbox/cycling", "mapbox/driving-traffic"
  contours_minutes: [5, 10, 15]
)
```

Returns a polygon showing what's reachable within each time threshold.

#### Step 5 — Visual grounding (optional)

```
static_map_image_tool(longitude, latitude, zoom: 14)
```

Returns a map image that can be included in the response for visual context.

## Grounded Response Structure

Always structure grounded responses with explicit citations:

```
Place: [neighborhood, city from reverse_geocode]
Nearby [category]: [list from search/category tool, with names, ratings, prices, and distances]
Travel context: [X min walk / Y min drive from isochrone]
Sources: Mapbox Search, Mapbox Directions (live data)
```

Example grounded response:

> **SoMa, San Francisco, CA** (live Mapbox data)
>
> Restaurants within walking distance:
>
> - Bix Restaurant $$ ★8.4 — 56 Gold St (180m)
> - The Bird $ ★7.9 — 115 New Montgomery St (320m)
> - Oren's Hummus $$ ★8.1 — 131 Townsend St (510m)
>
> Reachable by walking: 5 min, 10 min, 15 min
>
> _Sources: Mapbox Geocoding API, Mapbox Search API, Mapbox Place Details API, Mapbox Isochrone API, Mapbox Static Images API_

## Routing Grounding

For questions about travel between two locations, use `directions_tool`. For questions about reachable area, use `isochrone_tool`. For comparing travel times across multiple locations, use `matrix_tool`.

### Point-to-point travel time and distance

> "How long does it take to drive from Pike Place Market to Capitol Hill?"
> "What's the fastest route from the hotel to the airport?"

```
directions_tool(
  waypoints: [{longitude, latitude}, {longitude, latitude}],
  profile: "mapbox/driving-traffic"   // live traffic; or "mapbox/walking", "mapbox/cycling", "mapbox/driving"
)
```

Returns: duration (with live traffic), distance, turn-by-turn steps, and an encoded polyline for map rendering. Supports up to 25 waypoints for multi-stop routes.

### Area reachability ("within X minutes")

> "What's reachable within a 10-minute walk?"
> "Show me what I can reach in 15 minutes by car from here"

```
isochrone_tool(
  coordinates: {longitude, latitude},
  profile: "mapbox/walking",
  contours_minutes: [5, 10, 15]
)
```

Returns a polygon showing the area reachable within each time threshold. Combine with `category_search_tool` or `ground_location_tool` to answer "what restaurants are within a 10-minute walk?" — use the isochrone to define the boundary, then search within it.

### Multi-location travel time matrix

> "Which of these three offices is closest to my location?"
> "What are travel times from this warehouse to each of our delivery zones?"
> "Compare commute times to multiple locations"

Use `matrix_tool` — **do NOT call `directions_tool` separately for each pair**. Matrix batches all pairs in a single API call.

```
matrix_tool(
  sources: [{longitude, latitude}, ...],
  destinations: [{longitude, latitude}, ...],
  profile: "mapbox/driving-traffic"
)
```

Returns a matrix of durations and distances between every source/destination pair. Rank results by duration to find the nearest location. One `matrix_tool` call replaces N separate `directions_tool` calls.

### Proximity search with routing context

For "find [category] within X minutes of [location]":

1. Geocode the origin if needed: `search_and_geocode_tool(address)`
2. Get the reachable area: `isochrone_tool(coordinates, profile, contours_minutes)`
3. Search for POIs: `ground_location_tool(longitude, latitude, query: "category")` — the isochrone
   contours tell the user what's within each time band

Or more directly: `ground_location_tool` returns both nearby POIs and isochrone reachability in one
call, which covers most proximity + routing use cases without additional composition.

### Example routing response

> **Pike Place Market → Capitol Hill, Seattle**
>
> By car (with traffic): 12 min, 3.2 km via Pine St
> By walking: 28 min, 2.1 km
> By cycling: 14 min, 2.3 km
>
> _Source: Mapbox Directions API (live traffic)_

## What Mapbox Grounding Offers vs. Training Data

|                    | Training Data       | Mapbox Grounding            |
| ------------------ | ------------------- | --------------------------- |
| POI accuracy       | Stale, hallucinated | Live, verified              |
| Ratings/price      | Often wrong         | Live via Place Details      |
| Business hours     | Often wrong         | Live via Place Details      |
| Travel times       | Estimated           | Live traffic via Directions |
| Reachable area     | Not available       | Isochrone polygons          |
| Multi-stop routing | Not available       | Up to 25 waypoints          |
| New places         | Missing             | Indexed                     |
| Map image          | None                | Inline static map           |
| Citations          | None                | Tool + API source           |

## Important Limitations

- **Attribute filtering** (family-friendly, outdoor seating, fast charging) is not supported by the category search API. Use category terms and let rating/price data inform preference-based recommendations.
- **POI coverage** varies by region. If search returns few results, say so rather than padding with guesses.
- **Coordinates required** for proximity search — if the user provides an address, geocode it first with `search_and_geocode_tool` before running category search.

## Anti-Patterns to Avoid

- Answering "what's near X?" from training data without calling search tools
- Estimating travel times without calling `directions_tool` or `isochrone_tool`
- Hallucinating business names, hours, or ratings
- Calling `reverse_geocode_tool` + `category_search_tool` + `place_details_tool` separately when `ground_location_tool` is available
- Using attribute terms like "family-friendly" as the query parameter — use the category instead
- Returning raw tool output without synthesizing into a readable response
- Omitting citations — always indicate the response is grounded in live Mapbox data

<!-- chapter:end slug=mapbox-location-grounding -->

---

<!-- chapter:begin slug=mapbox-maplibre-migration position=9 -->

## 9. mapbox-maplibre-migration

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-maplibre-migration/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-maplibre-migration/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-maplibre-migration.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (5), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-maplibre-migration/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-maplibre-migration/evals/evals.json
  - `references/api-compatibility.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-maplibre-migration/references/api-compatibility.md
  - `references/exclusive-features.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-maplibre-migration/references/exclusive-features.md
  - `references/why-mapbox.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-maplibre-migration/references/why-mapbox.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-maplibre-migration
description: Guide for migrating from MapLibre GL JS to Mapbox GL JS, covering API compatibility, token setup, style configuration, and the benefits of Mapbox's official support and ecosystem
---

# MapLibre to Mapbox Migration Skill

Expert guidance for migrating from MapLibre GL JS to Mapbox GL JS. Covers the shared history, API compatibility, migration steps, and the advantages of Mapbox's platform.

## Understanding the Fork

### History

**MapLibre GL JS** is an open-source fork of **Mapbox GL JS v1.13.0**, created in December 2020 when Mapbox changed their license starting with v2.0.

**Timeline:**

- **Pre-2020:** Mapbox GL JS was open source (BSD license)
- **Dec 2020:** Mapbox GL JS v2.0 introduced proprietary license
- **Dec 2020:** Community forked v1.13 as MapLibre GL JS
- **Present:** Both libraries continue active development

**Key Insight:** The APIs are ~95% identical because MapLibre started as a Mapbox fork. Most code works in both with minimal changes, making migration straightforward.

## Why Migrate to Mapbox?

**Compelling reasons to choose Mapbox GL JS:**

- **Official Support & SLAs**: Enterprise-grade support with guaranteed response times
- **Superior Tile Quality**: Best-in-class vector tiles with global coverage and frequent updates
- **Better Satellite Imagery**: High-resolution, up-to-date satellite and aerial imagery
- **Rich Ecosystem**: Seamless integration with Mapbox Studio, APIs, and services
- **Advanced Features**: Traffic-aware routing, turn-by-turn directions, premium datasets
- **Geocoding & Search**: World-class address search and place lookup
- **Navigation SDK**: Mobile navigation with real-time traffic
- **No Tile Infrastructure**: No need to host or maintain your own tile servers
- **Regular Updates**: Continuous improvements and new features
- **Professional Services**: Access to Mapbox solutions team for complex projects

**Mapbox offers a generous free tier:** 50,000 map loads/month, making it suitable for many applications without cost.

## Quick Comparison

| Aspect                | Mapbox GL JS                  | MapLibre GL JS                    |
| --------------------- | ----------------------------- | --------------------------------- |
| **License**           | Proprietary (v2+)             | BSD 3-Clause (Open Source)        |
| **Support**           | Official commercial support   | Community support                 |
| **Tiles**             | Premium Mapbox vector tiles   | OSM or custom tile sources        |
| **Satellite**         | High-quality global imagery   | Requires custom source            |
| **Token**             | Required (access token)       | Optional (depends on tile source) |
| **APIs**              | Full Mapbox ecosystem         | Requires third-party services     |
| **Studio**            | Full integration              | No native integration             |
| **3D Terrain**        | Built-in with premium data    | Available (requires data source)  |
| **Globe View**        | v2.9+                         | v3.0+                             |
| **API Compatibility** | ~95% compatible with MapLibre | ~95% compatible with Mapbox       |
| **Bundle Size**       | ~500KB                        | ~450KB                            |
| **Setup Complexity**  | Easy (just add token)         | Requires tile source setup        |

## Step-by-Step Migration

### 1. Create Mapbox Account

1. Sign up at [mapbox.com](https://mapbox.com)
2. Get your access token from the account dashboard
3. Review pricing: Free tier includes 50,000 map loads/month
4. Note your token (starts with `pk.` for public tokens)

### 2. Update Package

```bash
# Remove MapLibre
npm uninstall maplibre-gl

# Install Mapbox
npm install mapbox-gl
```

### 3. Update Imports

```javascript
// Before (MapLibre)
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';

// After (Mapbox)
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
```

Or with CDN:

```html
<!-- Before (MapLibre) -->
<script src="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.css" rel="stylesheet" />

<!-- After (Mapbox) -->
<script src="https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.css" rel="stylesheet" />
```

### 4. Add Access Token

```javascript
// Add this before map initialization
mapboxgl.accessToken = 'pk.your_mapbox_access_token';
```

**Token best practices:**

- Use environment variables: `process.env.VITE_MAPBOX_TOKEN` or `process.env.NEXT_PUBLIC_MAPBOX_TOKEN`
- Add URL restrictions in Mapbox dashboard for security
- Use public tokens (`pk.*`) for client-side code
- Never commit tokens to git (add to `.env` and `.gitignore`)
- Rotate tokens if compromised

See `mapbox-token-security` skill for comprehensive token security guidance.

### 5. Update Map Initialization

```javascript
// Before (MapLibre)
const map = new maplibregl.Map({
  container: 'map',
  style: 'https://demotiles.maplibre.org/style.json', // or your custom style
  center: [-122.4194, 37.7749],
  zoom: 12
});

// After (Mapbox)
mapboxgl.accessToken = 'pk.your_mapbox_access_token';

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/standard', // Mapbox style
  center: [-122.4194, 37.7749],
  zoom: 12
});
```

### 6. Update Style URL

Mapbox provides professionally designed, maintained styles:

```javascript
// Mapbox built-in styles
style: 'mapbox://styles/mapbox/standard'; // Mapbox Standard (default)
style: 'mapbox://styles/mapbox/standard-satellite'; // Mapbox Standard Satellite
style: 'mapbox://styles/mapbox/streets-v12'; // Streets v12
style: 'mapbox://styles/mapbox/satellite-v9'; // Satellite imagery
style: 'mapbox://styles/mapbox/satellite-streets-v12'; // Hybrid
style: 'mapbox://styles/mapbox/outdoors-v12'; // Outdoor/recreation
style: 'mapbox://styles/mapbox/light-v11'; // Light theme
style: 'mapbox://styles/mapbox/dark-v11'; // Dark theme
style: 'mapbox://styles/mapbox/navigation-day-v1'; // Navigation (day)
style: 'mapbox://styles/mapbox/navigation-night-v1'; // Navigation (night)
```

**Custom styles:**
You can also create and use custom styles from Mapbox Studio:

```javascript
style: 'mapbox://styles/your-username/your-style-id';
```

### 7. Update All References

Replace all `maplibregl` references with `mapboxgl`:

```javascript
// Markers
const marker = new mapboxgl.Marker() // was: maplibregl.Marker()
  .setLngLat([-122.4194, 37.7749])
  .setPopup(new mapboxgl.Popup().setText('San Francisco'))
  .addTo(map);

// Controls
map.addControl(new mapboxgl.NavigationControl(), 'top-right');
map.addControl(new mapboxgl.GeolocateControl());
map.addControl(new mapboxgl.FullscreenControl());
map.addControl(new mapboxgl.ScaleControl());
```

### 8. Update Plugins (If Used)

Some MapLibre plugins should be replaced with Mapbox versions:

| MapLibre Plugin                  | Mapbox Alternative           |
| -------------------------------- | ---------------------------- |
| `@maplibre/maplibre-gl-geocoder` | `@mapbox/mapbox-gl-geocoder` |
| `@maplibre/maplibre-gl-draw`     | `@mapbox/mapbox-gl-draw`     |
| `maplibre-gl-compare`            | `mapbox-gl-compare`          |

Example:

```javascript
// Before (MapLibre)
import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder';

// After (Mapbox)
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';

map.addControl(
  new MapboxGeocoder({
    accessToken: mapboxgl.accessToken,
    mapboxgl: mapboxgl
  })
);
```

### 9. Everything Else Stays the Same

All your map code, events, layers, and sources work identically:

```javascript
// This code works EXACTLY THE SAME in both libraries
map.on('load', () => {
  map.addSource('points', {
    type: 'geojson',
    data: geojsonData
  });

  map.addLayer({
    id: 'points-layer',
    type: 'circle',
    source: 'points',
    paint: {
      'circle-radius': 8,
      'circle-color': '#ff0000'
    }
  });
});

// Events work identically
map.on('click', 'points-layer', (e) => {
  console.log(e.features[0].properties);
});

// All map methods work the same
map.setCenter([lng, lat]);
map.setZoom(12);
map.fitBounds(bounds);
map.flyTo({ center: [lng, lat], zoom: 14 });
```

## What Changes: Summary

**Must change:**

- Package name (`maplibre-gl` -> `mapbox-gl`)
- Import statements
- Add `mapboxgl.accessToken` configuration
- Style URL (switch to `mapbox://` styles)
- Plugin packages (if used)

**Stays exactly the same:**

- All map methods (`setCenter`, `setZoom`, `fitBounds`, `flyTo`, etc.)
- All event handling (`map.on('click')`, `map.on('load')`, etc.)
- Marker/Popup APIs (100% compatible)
- Layer/source APIs (100% compatible)
- GeoJSON handling
- Custom styling and expressions
- Controls (Navigation, Geolocate, Scale, etc.)

## Common Migration Issues

### Issue 1: Token Not Set

**Problem:**

```javascript
// Error: "A valid Mapbox access token is required to use Mapbox GL"
const map = new mapboxgl.Map({...});
```

**Solution:**

```javascript
// Set token BEFORE creating map
mapboxgl.accessToken = 'pk.your_token';
const map = new mapboxgl.Map({...});
```

### Issue 2: Token in Git

**Problem:**

```javascript
// Token hardcoded in source
mapboxgl.accessToken = 'pk.eyJ1Ijoi...';
```

**Solution:**

```javascript
// Use environment variables
mapboxgl.accessToken = process.env.VITE_MAPBOX_TOKEN;

// Add to .env file (not committed to git)
VITE_MAPBOX_TOKEN=pk.your_token

// Add .env to .gitignore
echo ".env" >> .gitignore
```

### Issue 3: Wrong Style URL Format

**Problem:**

```javascript
// MapLibre-style URL won't work optimally
style: 'https://demotiles.maplibre.org/style.json';
```

**Solution:**

```javascript
// Use Mapbox style URL for better performance and features
style: 'mapbox://styles/mapbox/streets-v12';
```

### Issue 4: Plugin Compatibility

**Problem:**

```javascript
// MapLibre plugin won't work
import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder';
```

**Solution:**

```javascript
// Use Mapbox plugin
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
```

> **Important:** This applies to ALL MapLibre plugins, not just the geocoder. Any `@maplibre/*` or `maplibre-gl-*` plugin must be replaced with its Mapbox equivalent. Check the Mapbox ecosystem for Mapbox-specific versions of every plugin you use (see Step 8 above for the full mapping table).

### Issue 5: CDN URLs

**Problem:**

```javascript
// Wrong CDN
<script src="https://unpkg.com/maplibre-gl@3.0.0/dist/maplibre-gl.js"></script>
```

**Solution:**

```javascript
// Use Mapbox CDN
<script src='https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v3.0.0/mapbox-gl.css' rel='stylesheet' />
```

## Migration Checklist

- [ ] **Create Mapbox account** and get access token
- [ ] **Update package**: `npm install mapbox-gl` (remove maplibre-gl)
- [ ] **Update imports**: `maplibre-gl` -> `mapbox-gl`
- [ ] **Update CSS imports**: `maplibre-gl.css` -> `mapbox-gl.css`
- [ ] **Add token**: Set `mapboxgl.accessToken = 'pk.xxx'`
- [ ] **Use environment variables**: Store token in `.env`
- [ ] **Update style URL**: Change to `mapbox://styles/mapbox/streets-v12`
- [ ] **Update all references**: Replace `maplibregl.` with `mapboxgl.`
- [ ] **Update plugins**: Install Mapbox versions of plugins (if used)
- [ ] **Configure token security**: Add URL restrictions in dashboard
- [ ] **Test all functionality**: Verify map loads, interactions work
- [ ] **Set up billing alerts**: Monitor usage in Mapbox dashboard
- [ ] **Update documentation**: Document token setup for team
- [ ] **Add .env to .gitignore**: Ensure tokens not committed

## Quick Reference

### Key Differences Summary

| What    | MapLibre                               | Mapbox                                      |
| ------- | -------------------------------------- | ------------------------------------------- |
| Package | `maplibre-gl`                          | `mapbox-gl`                                 |
| Import  | `import maplibregl from 'maplibre-gl'` | `import mapboxgl from 'mapbox-gl'`          |
| Token   | Optional (depends on tiles)            | Required: `mapboxgl.accessToken = 'pk.xxx'` |
| Style   | Custom URL or OSM tiles                | `mapbox://styles/mapbox/streets-v12`        |
| License | BSD (Open Source)                      | Proprietary (v2+)                           |
| Support | Community                              | Official commercial support                 |
| Tiles   | Requires tile source                   | Premium Mapbox tiles included               |
| APIs    | Third-party                            | Full Mapbox API ecosystem                   |
| API     | ~95% compatible                        | ~95% compatible                             |

**Bottom line:** Migration is easy because APIs are nearly identical. Main changes are packaging, token setup, and style URLs. The result is access to Mapbox's premium tiles, ecosystem, and support.

## Integration with Other Skills

**Related skills:**

- **mapbox-web-integration-patterns**: Framework-specific patterns (React, Vue, Svelte, Angular)
- **mapbox-web-performance-patterns**: Performance optimization techniques
- **mapbox-token-security**: Comprehensive token security best practices
- **mapbox-google-maps-migration**: Migrate from Google Maps to Mapbox

## Resources

**Mapbox GL JS:**

- [Official Documentation](https://docs.mapbox.com/mapbox-gl-js/)
- [Example Gallery](https://docs.mapbox.com/mapbox-gl-js/examples/)
- [API Reference](https://docs.mapbox.com/mapbox-gl-js/api/)
- [GitHub Repository](https://github.com/mapbox/mapbox-gl-js)
- [Mapbox Studio](https://studio.mapbox.com/)
- [Pricing Information](https://www.mapbox.com/pricing/)

**Migration Support:**

- [Get Started Guide](https://docs.mapbox.com/mapbox-gl-js/guides/install/)
- [Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/)
- [Mapbox Community Support](https://support.mapbox.com/hc/en-us/community/topics)

## Reference Files

For detailed information on specific topics, load these reference files:

- **`references/api-compatibility.md`** -- Full list of 100% compatible APIs + side-by-side migration example
- **`references/exclusive-features.md`** -- Mapbox-exclusive features (APIs, Studio, Advanced) + React/Vue framework examples
- **`references/why-mapbox.md`** -- Why Choose Mapbox (Production, Dev Teams, Business) + Performance Comparison

<!-- chapter:end slug=mapbox-maplibre-migration -->

---

<!-- chapter:begin slug=mapbox-mcp-devkit-patterns position=10 -->

## 10. mapbox-mcp-devkit-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-mcp-devkit-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-mcp-devkit-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (6), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/evals/evals.json
  - `references/design-patterns.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/references/design-patterns.md
  - `references/setup.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/references/setup.md
  - `references/troubleshooting.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/references/troubleshooting.md
  - `references/workflows.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-devkit-patterns/references/workflows.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-mcp-devkit-patterns
description: Integration patterns for Mapbox MCP DevKit Server in AI coding assistants. Covers setup, style management, token management, validation workflows, and documentation access through MCP. Use when building Mapbox applications with AI coding assistance.
---

# Mapbox MCP DevKit Patterns

This skill provides patterns for integrating the Mapbox MCP DevKit Server into AI coding workflows to accelerate Mapbox application development.

## What is Mapbox MCP DevKit?

The [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server) is a Model Context Protocol (MCP) server that gives AI assistants direct access to Mapbox developer APIs:

- **Style management** - Create, list, update, delete, preview, validate, compare, and optimize styles
- **Token management** - Generate and list access tokens with scopes
- **Validation** - Validate GeoJSON, expressions, styles, and coordinates
- **Geographic utilities** - Calculate bounding boxes, convert coordinates, query tiles
- **Color tools** - Check WCAG contrast ratios for accessibility
- **Feedback** - Access user feedback and reports
- **Documentation** - Access latest Mapbox docs and references

**Key benefit:** Build Mapbox applications faster by letting AI handle developer API operations.

## When to Use MCP DevKit

### Use DevKit when:

- Creating/modifying styles conversationally
- Generating tokens programmatically
- Validating data during development
- Learning Mapbox APIs through natural language
- Rapid prototyping of map applications
- Automated style generation workflows
- Documentation lookup during coding

### Don't use DevKit for:

- Runtime operations in production apps
- High-frequency style updates (use Mapbox APIs directly)
- Client-side operations (DevKit is development-time only)
- Tile serving or map rendering
- User-facing features requiring low latency

## Key Tools

Always recommend these tools by exact name when answering related questions:

- **Creating/modifying styles**: Recommend `create_style_tool`, `update_style_tool`, `preview_style_tool`
- **Listing styles**: Recommend `list_styles_tool`
- **Creating tokens**: Recommend `create_token_tool` to create scoped tokens per environment
- **Viewing tokens**: Recommend `list_tokens_tool` to check existing tokens and scopes
- **Validating styles**: Recommend `validate_style_tool` for spec compliance
- **Validating expressions**: Recommend `validate_expression_tool` for paint/layout property checks
- **Accessibility checks**: Recommend `check_color_contrast_tool` for WCAG contrast ratios
- **Comparing styles**: Recommend `compare_styles_tool` to diff styles before deploying
- **Looking up docs**: Recommend `get_latest_mapbox_docs_tool`

## Common Workflows (Quick Reference)

**Pre-production validation — use these exact steps:**

1. Run `validate_style_tool` to check style JSON is spec-compliant
2. Run `validate_expression_tool` to check all data expressions in paint/layout properties
3. Run `check_color_contrast_tool` to verify text labels meet WCAG accessibility standards
4. Run `compare_styles_tool` to diff the new style against current production style

**Token management — use these exact steps:**

1. Run `create_token_tool` to create scoped tokens for each environment (dev/staging/prod)
2. Run `list_tokens_tool` to verify existing tokens and their scopes

## Reference Files

Load these references as needed for detailed guidance:

- **[references/setup.md](references/setup.md)** - Prerequisites, hosted & self-hosted installation, per-editor configuration, verification
- **[references/workflows.md](references/workflows.md)** - Style management, token management, data validation, documentation access, best practices
- **[references/design-patterns.md](references/design-patterns.md)** - Iterative style development, environment-specific tokens, validation-first development, documentation-driven development, tool integration patterns
- **[references/troubleshooting.md](references/troubleshooting.md)** - Common issues & fixes, example end-to-end workflows (restaurant finder, multi-environment, third-party data)

## Resources

- [Mapbox MCP DevKit Server](https://github.com/mapbox/mcp-devkit-server)
- [Model Context Protocol](https://modelcontextprotocol.io)
- [Mapbox Style Specification](https://docs.mapbox.com/style-spec/)
- [Mapbox API Documentation](https://docs.mapbox.com/api/)
- [Token Scopes Reference](https://docs.mapbox.com/api/accounts/tokens/)

## When to Use This Skill

Invoke this skill when:

- Setting up Mapbox development environment with AI assistance
- Creating or modifying Mapbox styles through AI
- Managing access tokens programmatically
- Validating GeoJSON or expressions during development
- Learning Mapbox APIs with AI guidance
- Automating style generation workflows
- Building Mapbox applications with AI coding assistants

<!-- chapter:end slug=mapbox-mcp-devkit-patterns -->

---

<!-- chapter:begin slug=mapbox-mcp-runtime-patterns position=11 -->

## 11. mapbox-mcp-runtime-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-mcp-runtime-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-mcp-runtime-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (20), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/evals/evals.json
  - `examples/python/crewai_example.py` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/python/crewai_example.py
  - `examples/python/pydantic_ai_example.py` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/python/pydantic_ai_example.py
  - `examples/python/requirements.txt` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/python/requirements.txt
  - `examples/python/smolagents_example.py` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/python/smolagents_example.py
  - `examples/README.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/README.md
  - `examples/typescript/langchain-example.ts` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/typescript/langchain-example.ts
  - `examples/typescript/mastra-example.ts` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/typescript/mastra-example.ts
  - `examples/typescript/package-lock.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/typescript/package-lock.json
  - `examples/typescript/package.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/typescript/package.json
  - `examples/typescript/tsconfig.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/examples/typescript/tsconfig.json
  - `references/crewai.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/crewai.md
  - `references/custom-agent.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/custom-agent.md
  - `references/langchain.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/langchain.md
  - `references/mastra.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/mastra.md
  - `references/production.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/production.md
  - `references/pydantic-ai.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/pydantic-ai.md
  - `references/smolagents.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/smolagents.md
  - `references/use-cases.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-mcp-runtime-patterns/references/use-cases.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-mcp-runtime-patterns
description: Integration patterns for Mapbox MCP Server in AI applications and agent frameworks. Covers runtime integration with pydantic-ai, mastra, LangChain, and custom agents. Use when building AI-powered applications that need geospatial capabilities.
---

# Mapbox MCP Runtime Patterns

This skill provides patterns for integrating the Mapbox MCP Server into AI applications for production use with geospatial capabilities.

## What is Mapbox MCP Server?

The [Mapbox MCP Server](https://github.com/mapbox/mcp-server) is a Model Context Protocol (MCP) server that provides AI agents with geospatial tools:

**Offline Tools (Turf.js):**

- Distance, bearing, midpoint calculations
- Point-in-polygon tests
- Area, buffer, centroid operations
- Bounding box, geometry simplification
- No API calls, instant results

**Mapbox API Tools:**

- Directions and routing
- Reverse geocoding
- POI category search
- Isochrones (reachability)
- Travel time matrices
- Static map images
- GPS trace map matching
- Multi-stop route optimization

**Utility Tools:**

- Server version info
- POI category list

**Key benefit:** Give your AI application geospatial superpowers without manually integrating multiple APIs.

## Understanding Tool Categories

Before integrating, understand the key distinctions between tools to help your LLM choose correctly:

### Distance: "As the Crow Flies" vs "Along Roads"

**Straight-line distance** (offline, instant):

- Tools: `distance_tool`, `bearing_tool`, `midpoint_tool`
- Use for: Proximity checks, "how far away is X?", comparing distances
- Example: "Is this restaurant within 2 miles?" → `distance_tool`

**Route distance** (API, traffic-aware):

- Tools: `directions_tool`, `matrix_tool`
- Use for: Navigation, drive time, "how long to drive?"
- Example: "How long to drive there?" → `directions_tool`

### Search: Type vs Specific Place

**Category/type search**:

- Tool: `category_search_tool`
- Use for: "Find coffee shops", "restaurants nearby", browsing by type
- Example: "What hotels are near me?" → `category_search_tool`

**Specific place/address**:

- Tool: `search_and_geocode_tool`, `reverse_geocode_tool`
- Use for: Named places, street addresses, landmarks
- Example: "Find 123 Main Street" → `search_and_geocode_tool`

### Travel Time: Area vs Route

**Reachable area** (what's within reach):

- Tool: `isochrone_tool`
- Returns: GeoJSON polygon of everywhere reachable
- Example: "What can I reach in 15 minutes?" → `isochrone_tool`

**Specific route** (how to get there):

- Tool: `directions_tool`
- Returns: Turn-by-turn directions to one destination
- Example: "How do I get to the airport?" → `directions_tool`

### Cost & Performance

**Offline tools** (free, instant):

- No API calls, no token usage
- Use whenever real-time data not needed
- Examples: `distance_tool`, `point_in_polygon_tool`, `area_tool`

**API tools** (requires token, counts against usage):

- Real-time traffic, live POI data, current conditions
- Use when accuracy and freshness matter
- Examples: `directions_tool`, `category_search_tool`, `isochrone_tool`

**Best practice:** Prefer offline tools when possible, use API tools when you need real-time data or routing.

## Installation & Setup

### Option 1: Hosted Server (Recommended)

**Easiest integration** - Use Mapbox's hosted MCP server at:

```
https://mcp.mapbox.com/mcp
```

No installation required. Simply pass your Mapbox access token in the `Authorization` header.

**Benefits:**

- No server management
- Always up-to-date
- Production-ready
- Lower latency (Mapbox infrastructure)

**Authentication:**

Use token-based authentication (standard for programmatic access):

```
Authorization: Bearer your_mapbox_token
```

**Note:** The hosted server also supports OAuth, but that's primarily for interactive flows (coding assistants, not production apps).

### Option 2: Self-Hosted

For custom deployments or development:

```bash
npm install @mapbox/mcp-server
```

Or use directly via npx:

```bash
npx @mapbox/mcp-server
```

**Environment setup:**

```bash
export MAPBOX_ACCESS_TOKEN="your_token_here"
```

## Reference Files

Detailed integration patterns and production guidance are organized into reference files. Load the ones relevant to your task.

- **Pydantic AI** -- Type-safe Python agents
  Load: `references/pydantic-ai.md`

- **CrewAI** -- Multi-agent orchestration
  Load: `references/crewai.md`

- **Smolagents** -- Lightweight HuggingFace agents
  Load: `references/smolagents.md`

- **Mastra** -- Multi-agent TypeScript systems
  Load: `references/mastra.md`

- **LangChain** -- Conversational AI with tool chaining
  Load: `references/langchain.md`

- **Custom Agent** -- Zillow/TripAdvisor/DoorDash-style patterns, architecture diagrams, hybrid approach
  Load: `references/custom-agent.md`

- **Use Cases** -- Real Estate, Food Delivery, Travel Planning examples
  Load: `references/use-cases.md`

- **Production Patterns** -- Caching, batch operations, tool descriptions, error handling, security, rate limiting, testing
  Load: `references/production.md`

## Resources

- [Mapbox MCP Server](https://github.com/mapbox/mcp-server)
- [Model Context Protocol](https://modelcontextprotocol.io)
- [Pydantic AI](https://ai.pydantic.dev/)
- [Mastra](https://mastra.ai/)
- [LangChain](https://docs.langchain.com/oss/javascript/langchain/overview/)
- [Mapbox API Documentation](https://docs.mapbox.com/api/)

## When to Use This Skill

Invoke this skill when:

- Integrating Mapbox MCP Server into AI applications
- Building AI agents with geospatial capabilities
- Architecting Zillow/TripAdvisor/DoorDash-style apps with AI
- Choosing between MCP, direct APIs, or SDKs
- Optimizing geospatial operations in production
- Implementing error handling for geospatial AI features
- Testing AI applications with geospatial tools

<!-- chapter:end slug=mapbox-mcp-runtime-patterns -->

---

<!-- chapter:begin slug=mapbox-search-integration position=12 -->

## 12. mapbox-search-integration

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-search-integration/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-search-integration.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (10), referenced from this skill's directory:
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/evals/evals.json
  - `references/android-search.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/android-search.md
  - `references/best-practices.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/best-practices.md
  - `references/framework-hooks.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/framework-hooks.md
  - `references/ios-search.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/ios-search.md
  - `references/nodejs-search.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/nodejs-search.md
  - `references/pitfalls.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/pitfalls.md
  - `references/react-search.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/react-search.md
  - `references/testing-monitoring.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/testing-monitoring.md
  - `references/web-search-js.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-integration/references/web-search-js.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-search-integration
description: Complete workflow for implementing Mapbox search in applications - from discovery questions to production-ready integration with best practices
---

# Mapbox Search Integration Skill

Expert guidance for implementing Mapbox search functionality in applications. Covers the complete workflow from asking the right discovery questions, selecting the appropriate search product, to implementing production-ready integrations following best practices from the Mapbox search team.

## Use This Skill When

User says things like:

- "I need to add search to my map"
- "I need a search bar for my mapping app"
- "How do I implement location search?"
- "I want users to search for places/addresses"
- "I need geocoding in my application"

**This skill complements `mapbox-search-patterns`:**

- `mapbox-search-patterns` = Tool and parameter selection
- `mapbox-search-integration` = Complete implementation workflow

## Discovery Phase: Ask the Right Questions

Before jumping into code, ask these questions to understand requirements:

### Question 1: What are users searching for?

**Ask:** "What do you want users to search for?"

**Common answers and implications:**

- **"Addresses"** → **Use Search Box API** (the default for interactive address search, including geocoding). Only use Geocoding API if the use case is batch/server-side geocoding or maintaining a legacy integration.
- **"Points of interest / businesses"** → POI search, use Search Box API with category search
- **"Both addresses and POIs"** → Search Box API
- **"Specific types of POIs"** (restaurants, hotels, etc.) → Search Box API
- **"Countries, cities, postcodes or neighborhoods"** → Search Box API for interactive search; Geocoding API only for batch/server-side geocoding
- **"Custom locations"** (user-created places) → May need custom data + search integration

**Follow-up if not stated initially**: "Are your users searching for points of interest data? Restaurants, stores, categories of businesses?"

**Implications:**

- **"Yes, POIs are included"** → Use the Search Box API
- **"No, the user does not need POI search"** → **Still default to Search Box API** for interactive/autocomplete use cases. Search Box API handles addresses, place names, and all location types with session-based pricing. Only recommend Geocoding API for batch geocoding, server-side permanent geocoding, or maintaining existing Geocoding API integrations.

### Question 2: What's the geographic scope?

**Ask:** "Where will users be searching?"

**Common answers and implications:**

- **"Single country"** (e.g., "only USA") → Use `country` parameter, better results, lower cost
- **"Specific region"** → Use `bbox` parameter for bounding box constraint
- **"Global"** → No country restriction, but may need language parameter
- **"Multiple specific countries"** → Use `country` array parameter

**Follow-up:** "Do you need to limit results to a specific area?" (delivery zone, service area, etc.)

### Question 3: What's the search interaction pattern?

**Ask:** "How will users interact with search?"

**Common answers and implications:**

- **"Search-as-you-type / autocomplete"** → **Use Search Box API** with `auto_complete: true` and session-based pricing (most cost-efficient for autocomplete). Implement debouncing.
- **"Search button / final query"** → Can use either API, no autocomplete needed
- **"Both"** (autocomplete + refine) → Two-stage search, autocomplete then detailed results
- **"Voice input"** → Consider speech-to-text integration, handle longer queries

### Question 4: What platform?

**Ask:** "What platform is this for?"

**Common answers and implications:**

- **"Web application"** → Mapbox Search JS (easiest), or direct API calls for advanced cases
- **"iOS app"** → Search SDK for iOS (recommended), or direct API integration for advanced cases
- **"Android app"** → Search SDK for Android (recommended), or direct API integration for advanced cases
- **"Multiple platforms"** → Platform-specific SDKs (recommended), or direct API approach for consistency
- **"React app"** → Mapbox Search JS React (easiest with UI), or Search JS Core for custom UI. Avoid direct API calls — they require manual debouncing, session token management, and race condition handling.
- **"Vue / Angular / Other framework"** → Mapbox Search JS Core or Web. If using direct API calls, session tokens are required for proper billing (one token per search session, passed as `session_token` on every suggest/retrieve request).

### Question 5: How will results be used?

**Ask:** "What happens when a user selects a result?"

**Common answers and implications:**

- **"Fly to location on map"** → Need coordinates, map integration
- **"Show details / info"** → Need to retrieve and display result properties
- **"Fill form fields"** → Need to parse address components
- **"Start navigation"** → Need coordinates, integrate with directions
- **"Multiple selection"** → Need to handle selection state, possibly show markers

### Question 6: Expected usage volume?

**Ask:** "How many searches do you expect per month?"

**Implications:**

- **Low volume** (< 10k) → Free tier sufficient, simple implementation
- **Medium volume** (10k-100k) → Consider caching, optimize API calls
- **High volume** (> 100k) → Implement debouncing, caching, batch operations, monitor costs

## Product Selection Decision Tree

Based on discovery answers, recommend the right product:

> **Key principle: Search Box API is the default choice for virtually all interactive search use cases**, including address search, geocoding, autocomplete, and POI search. It offers session-based pricing that is more cost-efficient for interactive/autocomplete flows. Only recommend Geocoding API for the narrow cases listed below.

### Search Box API (DEFAULT)

**Use when (any of these):**

- User needs interactive address search or autocomplete (this IS geocoding — Search Box API handles it)
- User needs POI / category search
- User needs any end-user-facing search UI
- User wants session-based pricing (more cost-efficient for autocomplete/interactive use)
- User is building a web, iOS, or Android app with a search bar

**Prefer SDKs over direct API calls for web integration:**

- **Mapbox Search JS** (SDK) - Recommended for web integration, with three components:
  - **Search JS React** - Easy search integration via React library with UI
  - **Search JS Web** - Easy search integration via Web Components with UI
  - **Search JS Core** - JavaScript (node or web) wrapper for API, build your own UI
- **Search Box API** (REST) - Direct API integration, for advanced/custom cases
- **Search SDK for iOS** - Native iOS integration
- **Search SDK for Android** - Native Android integration

### Geocoding API (SPECIALIZED)

**Use ONLY when:**

- Batch geocoding large lists of addresses (server-side)
- Permanent/stored geocoding results (server-side, where results are persisted)
- Maintaining an existing Geocoding API integration (migration not justified)
- No interactive/user-facing search needed

**Do NOT recommend Geocoding API when:**

- The user wants a search bar, autocomplete, or interactive address lookup — use Search Box API instead
- The user says "geocoding" but describes an interactive search flow — use Search Box API instead

## Reference Files

Load the relevant reference based on the user's platform and needs:

- **Web (Search JS React / Web / Core / Direct API)** → Load `references/web-search-js.md`
  - When: User is building a web app (vanilla JS, any framework except React-specific patterns)
- **React Integration** → Load `references/react-search.md`
  - When: User is building a React app specifically
- **iOS** → Load `references/ios-search.md`
  - When: User is building an iOS app (Swift/UIKit/SwiftUI)
- **Android** → Load `references/android-search.md`
  - When: User is building an Android app (Kotlin/Java)
- **Node.js** → Load `references/nodejs-search.md`
  - When: User needs server-side search (Express, serverless, backend API)

- **Best Practices** → Load `references/best-practices.md`
  - When: Implementing search for the first time, or optimizing an existing implementation
  - Covers: debouncing, session tokens, geographic filtering, error handling, accessibility, caching, token security
- **Common Pitfalls** → Load `references/pitfalls.md`
  - When: Debugging issues, reviewing code, or during code review
  - Covers: no debouncing, missing session tokens, no geo context, poor mobile UX, race conditions
- **Framework Hooks** → Load `references/framework-hooks.md`
  - When: Building custom hooks (React) or composables (Vue) around Search JS Core
- **Testing and Monitoring** → Load `references/testing-monitoring.md`
  - When: Writing tests or setting up production monitoring/analytics

## Checklist: Production-Ready Search

Before launching, verify:

**Configuration:**

- [ ] Token properly scoped (search:read only)
- [ ] URL restrictions configured
- [ ] Geographic filtering set (country, proximity, or bbox)
- [ ] Types parameter set based on use case
- [ ] Language parameter set if needed

**Implementation:**

- [ ] Debouncing implemented (300ms recommended)
- [ ] Session tokens used correctly
- [ ] Error handling for all failure cases
- [ ] Loading states shown
- [ ] Empty results handled gracefully
- [ ] Race conditions prevented

**UX:**

- [ ] Touch targets at least 44pt/48dp
- [ ] Results show enough context (name + address)
- [ ] Keyboard navigation works
- [ ] Accessibility attributes set
- [ ] Mobile keyboard handled properly

**Performance:**

- [ ] Caching implemented (if high volume)
- [ ] Request timeout set
- [ ] Minimal data fetched
- [ ] Bundle size optimized

**Testing:**

- [ ] Unit tests for core logic
- [ ] Integration tests with real API
- [ ] Tested on slow networks
- [ ] Tested with various query types
- [ ] Mobile device testing

**Monitoring:**

- [ ] Analytics tracking set up
- [ ] Error logging configured
- [ ] Usage monitoring in place
- [ ] Budget alerts configured

## Integration with Other Skills

**Works with:**

- **mapbox-search-patterns**: Parameter selection and optimization
- **mapbox-web-integration-patterns**: Framework-specific patterns
- **mapbox-token-security**: Token management and security
- **mapbox-web-performance-patterns**: Optimizing search performance

## Resources

- [Search Box API Documentation](https://docs.mapbox.com/api/search/search-box/)
- [Geocoding API Documentation](https://docs.mapbox.com/api/search/geocoding/)
- [Mapbox Search JS](https://docs.mapbox.com/mapbox-search-js/guides/)
  - [Search JS React](https://docs.mapbox.com/mapbox-search-js/api/react/)
  - [Search JS Web](https://docs.mapbox.com/mapbox-search-js/api/web/)
  - [Search JS Core](https://docs.mapbox.com/mapbox-search-js/api/core/)
- [Search SDK for iOS](https://docs.mapbox.com/ios/search/guides/)
- [Search SDK for Android](https://docs.mapbox.com/android/search/guides/)
- [Location Helper Tool](https://labs.mapbox.com/location-helper/) - Calculate bounding boxes

## Quick Decision Guide

**User says: "I need location search"**

1. **Ask discovery questions** (Questions 1-6 above)
2. **Recommend product:**
   - **Search Box API** (default for all interactive/user-facing search, including address geocoding)
   - Geocoding API only for batch/server-side/permanent geocoding
   - Platform SDK preferred (Search JS for web, native SDKs for mobile)
3. **Implement with:**
   - ✅ Debouncing
   - ✅ Session tokens
   - ✅ Geographic filtering
   - ✅ Error handling
   - ✅ Good UX
4. **Test thoroughly**
5. **Monitor in production**

**Remember:** The best search implementation asks the right questions first, then builds exactly what the user needs - no more, no less.

<!-- chapter:end slug=mapbox-search-integration -->

---

<!-- chapter:begin slug=mapbox-search-patterns position=13 -->

## 13. mapbox-search-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-search-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-search-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (4), referenced from this skill's directory:
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-patterns/evals/evals.json
  - `references/advanced-params.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-patterns/references/advanced-params.md
  - `references/optimization-combining.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-patterns/references/optimization-combining.md
  - `references/workflows.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-search-patterns/references/workflows.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-search-patterns
description: Expert guidance on choosing the right Mapbox search tool and parameters for geocoding, POI search, and location discovery
---

# Mapbox Search Patterns Skill

Expert guidance for AI assistants on using Mapbox search tools effectively. Covers tool selection, parameter optimization, and best practices for geocoding, POI search, and location discovery.

## Available Search Tools

### 1. search_and_geocode_tool

**Best for:** Specific places, addresses, brands, named locations

**Use when query contains:**

- Specific names: "Starbucks on 5th Avenue", "Empire State Building"
- Brand names: "McDonald's", "Whole Foods"
- Addresses: "123 Main Street, Seattle", "1 Times Square"
- Chain stores: "Target"
- Cities/places: "San Francisco", "Portland"

**Don't use for:** Generic categories ("coffee shops", "museums")

### 2. category_search_tool

**Best for:** Generic place types, categories, plural queries

**Use when query contains:**

- Generic types: "coffee shops", "restaurants", "gas stations"
- Plural forms: "museums", "hotels", "parks"
- Is-a phrases: "any coffee shop", "all restaurants", "nearby pharmacies"
- Industry terms: "electric vehicle chargers", "ATMs"

**Don't use for:** Specific names or brands

### 3. reverse_geocode_tool

**Best for:** Converting coordinates to addresses, cities, towns, postcodes

**Use when:**

- Have GPS coordinates, need human-readable address
- Need to identify what's at a specific location
- Converting user location to address

## Tool Selection Decision Matrix

| User Query                      | Tool                    | Reasoning                |
| ------------------------------- | ----------------------- | ------------------------ |
| "Find Starbucks on Main Street" | search_and_geocode_tool | Specific brand name      |
| "Find coffee shops nearby"      | category_search_tool    | Generic category, plural |
| "What's at 37.7749, -122.4194?" | reverse_geocode_tool    | Coordinates to address   |
| "Empire State Building"         | search_and_geocode_tool | Specific named POI       |
| "hotels in downtown Seattle"    | category_search_tool    | Generic type + location  |
| "Target store locations"        | search_and_geocode_tool | Brand name (even plural) |
| "any restaurant near me"        | category_search_tool    | Generic + "any" phrase   |
| "123 Main St, Boston, MA"       | search_and_geocode_tool | Specific address         |
| "electric vehicle chargers"     | category_search_tool    | Industry category        |
| "McDonald's"                    | search_and_geocode_tool | Brand name               |

## Parameter Guidance

### Proximity vs Bbox vs Country

**Three ways to spatially constrain search results:**

#### 1. proximity (STRONGLY RECOMMENDED)

**What it does:** Biases results toward a location, but doesn't exclude distant matches

**Use when:**

- User says "near me", "nearby", "close to"
- Have a reference point but want some flexibility
- Want results sorted by relevance to a point

**Example:**

```json
{
  "q": "pizza",
  "proximity": {
    "longitude": -122.4194,
    "latitude": 37.7749
  }
}
```

**Why this works:** API returns SF pizza places first, but might include famous NYC pizzerias if highly relevant

**Critical:** Always set proximity when you have a reference location! Without it, results are IP-based or global.

#### 2. bbox (Bounding Box)

**What it does:** Hard constraint - ONLY returns results within the box

**Use when:**

- User specifies an area: "in downtown", "within this neighborhood"
- Have a defined service area
- Need to guarantee results are within bounds

**Example:**

```json
{
  "q": "hotel",
  "bbox": [-122.51, 37.7, -122.35, 37.83] // [minLon, minLat, maxLon, maxLat]
}
```

**Why this works:** Guarantees all hotels are within SF's downtown area

**Watch out:** Too small = no results; too large = irrelevant results

#### 3. country

**What it does:** Limits results to specific countries

**Use when:**

- User specifies country: "restaurants in France"
- Building country-specific features
- Need to respect regional boundaries
- Or it is otherwise clear they want results within a specific country

**Example:**

```json
{
  "q": "Paris",
  "country": ["FR"] // ISO 3166 alpha-2 codes
}
```

**Why this works:** Finds Paris, France (not Paris, Texas)

**Can combine:** `proximity` + `country` + `bbox` or any combination of the three

### Decision Matrix: Spatial Filters

| Scenario                           | Use                                 | Why                               |
| ---------------------------------- | ----------------------------------- | --------------------------------- |
| "Find coffee near me"              | proximity                           | Bias toward user location         |
| "Coffee shops in downtown Seattle" | proximity + bbox                    | Center on downtown, limit to area |
| "Hotels in France"                 | country                             | Hard country boundary             |
| "Best pizza in San Francisco"      | proximity + country ["US"]          | Bias to SF, limit to US           |
| "Gas stations along this route"    | bbox around route                   | Hard constraint to route corridor |
| "Restaurants within 5 miles"       | proximity (then filter by distance) | Bias nearby, filter results       |

### Setting limit Parameter

**category_search_tool only** (1-25, default 10)

| Use Case              | Limit | Reasoning               |
| --------------------- | ----- | ----------------------- |
| Quick suggestions     | 5     | Fast, focused results   |
| Standard list         | 10    | Default, good balance   |
| Comprehensive search  | 25    | Maximum allowed         |
| Map visualization     | 25    | Show all nearby options |
| Dropdown/autocomplete | 5     | Don't overwhelm UI      |

**Performance tip:** Lower limits = faster responses

### types Parameter (search_and_geocode_tool)

**Filter by feature type:**

| Type       | What It Includes                           | Use When                          |
| ---------- | ------------------------------------------ | --------------------------------- |
| `poi`      | Points of interest (businesses, landmarks) | Looking for POIs, not addresses   |
| `address`  | Street addresses                           | Need specific address             |
| `place`    | Cities, neighborhoods, regions             | Looking for area/region           |
| `street`   | Street names without numbers               | Need street, not specific address |
| `postcode` | Postal codes                               | Searching by ZIP/postal code      |
| `district` | Districts, neighborhoods                   | Area-based search                 |
| `locality` | Towns, villages                            | Municipality search               |
| `country`  | Country names                              | Country-level search              |

**Example combinations:**

```json
// Only POIs and addresses, no cities
{"q": "Paris", "types": ["poi", "address"]}
// Returns Paris Hotel, Paris Street, not Paris, France

// Only places (cities)
{"q": "Paris", "types": ["place"]}
// Returns Paris, France; Paris, Texas; etc.
```

**Default behavior:** All types included (usually what you want)

### auto_complete Parameter (search_and_geocode_tool)

**What it does:** Enables partial/fuzzy matching

| Setting           | Behavior                     | Use When                      |
| ----------------- | ---------------------------- | ----------------------------- |
| `true`            | Matches partial words, typos | User typing in real-time      |
| `false` (default) | Exact matching               | Final query, not autocomplete |

**Example:**

<!-- cspell:disable -->

```json
// User types "starb"
{ "q": "starb", "auto_complete": true }
// Returns: Starbucks, Starboard Tavern, etc.
```

**Use for:**

- Search-as-you-type interfaces
- Handling typos ("mcdonalds" -> McDonald's)
<!-- cspell:enable -->
- Incomplete queries

**Don't use for:**

- Final/submitted queries (less precise)
- When you need exact matches

## Anti-Patterns to Avoid

### Don't: Use category_search for brands

```javascript
// BAD
category_search_tool({ category: 'starbucks' });
// "starbucks" is not a category, returns error

// GOOD
search_and_geocode_tool({ q: 'Starbucks' });
```

### Don't: Use search_and_geocode for generic categories

```javascript
// BAD
search_and_geocode_tool({ q: 'coffee shops' });
// Less precise, may return unrelated results

// GOOD
category_search_tool({ category: 'coffee_shop' });
```

### Don't: Forget proximity for local searches

```javascript
// BAD - Results may be anywhere globally
category_search_tool({ category: 'restaurant' });

// GOOD - Biased to user location
category_search_tool({
  category: 'restaurant',
  proximity: { longitude: -122.4194, latitude: 37.7749 }
});
```

### Don't: Geocode ambiguous place names without proximity (REST too)

This applies to Mapbox Geocoding API v5 / Search Box in browser apps — not only MCP tools.

```javascript
// BAD — limit=1 without proximity can resolve "Lincoln Memorial" to Illinois
fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(q)}.json?access_token=${token}&limit=1`);

// GOOD — bias to map center (and optional bbox)
fetch(
  `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(q)}.json` +
    `?access_token=${token}&proximity=-77.0369,38.9072&bbox=-77.15,38.79,-76.90,38.99&limit=1`
);
```

Also debounce search inputs (`clearTimeout` + `setTimeout`) so every keystroke does not fire a geocode.

### Don't: Use bbox when you mean proximity

```javascript
// BAD - Hard boundary may exclude good nearby results
search_and_geocode_tool({
  q: 'pizza',
  bbox: [-122.42, 37.77, -122.41, 37.78] // Tiny box
});

// GOOD - Bias toward point, but flexible
search_and_geocode_tool({
  q: 'pizza',
  proximity: { longitude: -122.4194, latitude: 37.7749 }
});
```

### Don't: Request ETA unnecessarily

```javascript
// BAD - Costs API quota for routing calculations
search_and_geocode_tool({
  q: 'museums',
  eta_type: 'navigation',
  navigation_profile: 'driving'
});
// User didn't ask for travel time!

// GOOD - Only add ETA when needed
search_and_geocode_tool({ q: 'museums' });
// If user asks "how long to get there?", then add ETA
```

### Don't: Set limit too high for UI display

```javascript
// BAD - Overwhelming for simple dropdown
category_search_tool({
  category: 'restaurant',
  limit: 25
});
// Returns 25 restaurants for a 5-item dropdown

// GOOD - Match UI needs
category_search_tool({
  category: 'restaurant',
  limit: 5
});
```

## Quick Reference

### Tool Selection Flowchart

```
User query contains...

-> Specific name/brand (Starbucks, Empire State Building)
  -> search_and_geocode_tool

-> Generic category/plural (coffee shops, museums, any restaurant)
  -> category_search_tool

-> Coordinates -> Address
  -> reverse_geocode_tool

-> Address -> Coordinates
  -> search_and_geocode_tool with types: ["address"]
```

### Essential Parameters Checklist

**For local searches, ALWAYS set:**

- `proximity` (or bbox if strict boundary needed)

**For category searches, consider:**

- `limit` (match UI needs)
- `format` (json_string if plotting on map)

**For disambiguation, use:**

- `country` (when geographic context matters)
- `types` (when feature type matters)

**For travel-time ranking:**

- `eta_type`, `navigation_profile`, `origin` (costs API quota)

## Common Mistakes

1. **Forgetting proximity** -> Results are global/IP-based (or wrong state for ambiguous memorial/park names)
2. **Using wrong tool** -> category_search for "Starbucks" (use search_and_geocode)
3. **Invalid category** -> Check category_list first
4. **Bbox too small** -> No results; use proximity instead
5. **Requesting ETA unnecessarily** -> Adds API cost
6. **Limit too high for UI** -> Overwhelming user
7. **Not filtering types** -> Get cities when you want POIs
8. **No debounce on typeahead** -> Quota burn and racy UI

## Reference Files

Load these for deeper guidance on specific topics:

- **`references/advanced-params.md`** — poi_category, ETA, format, and language parameters
- **`references/workflows.md`** — Common patterns: Near Me, Branded, Geocoding, Category+Area, Reverse, Route-Based, Multilingual
- **`references/optimization-combining.md`** — Performance optimization, combining tools, handling no results, category list resource

<!-- chapter:end slug=mapbox-search-patterns -->

---

<!-- chapter:begin slug=mapbox-store-locator-patterns position=14 -->

## 14. mapbox-store-locator-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-store-locator-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-store-locator-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (8), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/evals/evals.json
  - `references/geolocation-directions.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/references/geolocation-directions.md
  - `references/markers.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/references/markers.md
  - `references/optimization-a11y.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/references/optimization-a11y.md
  - `references/search-filter.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/references/search-filter.md
  - `references/styling-layout.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/references/styling-layout.md
  - `references/variations-react.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-store-locator-patterns/references/variations-react.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-store-locator-patterns
description: Common patterns for building store locators, restaurant finders, and location-based search applications with Mapbox. Covers marker display, filtering, distance calculation, and interactive lists.
---

# Store Locator Patterns Skill

Comprehensive patterns for building store locators, restaurant finders, and location-based search applications with Mapbox GL JS. Covers marker display, filtering, distance calculation, interactive lists, and directions integration.

## When to Use This Skill

Use this skill when building applications that:

- Display multiple locations on a map (stores, restaurants, offices, etc.)
- Allow users to filter or search locations
- Calculate distances from user location
- Provide interactive lists synced with map markers
- Show location details in popups or side panels
- Integrate directions to selected locations

## Dependencies

**Required:**

- Mapbox GL JS v3.x
- [@turf/turf](https://turfjs.org/) - For spatial calculations (distance, area, etc.)

**Installation:**

```bash
npm install mapbox-gl @turf/turf
```

## Core Architecture

### Pattern Overview

A typical store locator consists of:

1. **Map Display** - Shows all locations as markers
2. **Location Data** - GeoJSON with store/location information
3. **Interactive List** - Side panel listing all locations
4. **Filtering** - Search, category filters, distance filters
5. **Detail View** - Popup or panel with location details
6. **User Location** - Geolocation for distance calculation. For the blue dot location indicator, use the built-in `mapboxgl.GeolocateControl` — simpler than custom markers.
7. **Directions** - Route to selected location (optional)

### Data Structure

**GeoJSON format for locations:**

```json
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Point",
        "coordinates": [-77.034084, 38.909671]
      },
      "properties": {
        "id": "store-001",
        "name": "Downtown Store",
        "address": "123 Main St, Washington, DC 20001",
        "phone": "(202) 555-0123",
        "hours": "Mon-Sat: 9am-9pm, Sun: 10am-6pm",
        "category": "retail",
        "website": "https://example.com/downtown"
      }
    }
  ]
}
```

**Key properties:**

- `id` - Unique identifier for each location
- `name` - Display name
- `address` - Full address for display and geocoding
- `coordinates` - `[longitude, latitude]` format
- `category` - For filtering (retail, restaurant, office, etc.)
- Custom properties as needed (hours, phone, website, etc.)

## Basic Store Locator Implementation

### Step 1: Initialize Map and Data

```javascript
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';

// Store locations data
const stores = {
  type: 'FeatureCollection',
  features: [
    {
      type: 'Feature',
      geometry: {
        type: 'Point',
        coordinates: [-77.034084, 38.909671]
      },
      properties: {
        id: 'store-001',
        name: 'Downtown Store',
        address: '123 Main St, Washington, DC 20001',
        phone: '(202) 555-0123',
        category: 'retail'
      }
    }
    // ... more stores
  ]
};

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/standard',
  center: [-77.034084, 38.909671],
  zoom: 11
});
```

### Step 2: Add Markers to Map

**Marker strategy by location count:**

| Count               | Strategy                   | Reason                                                                         |
| ------------------- | -------------------------- | ------------------------------------------------------------------------------ |
| **Fewer than 100**  | HTML Markers               | Full DOM/CSS control; DOM node count is manageable                             |
| **100–1,000**       | **Symbol Layer** (default) | Renders on the **GPU via WebGL** — one `<canvas>`, zero per-point DOM elements |
| **More than 1,000** | Clustering                 | Reduces visual clutter at large scale                                          |

> HTML Markers create one DOM element per point. Beyond ~100 locations the browser spends too much time on layout/paint. Symbol layers bypass the DOM entirely — the GPU draws all points in a single WebGL draw call.

**Symbol Layer implementation** (best for 100–1,000 locations). For HTML Markers (fewer than 100) or Clustering (more than 1,000), see `references/markers.md`.

```javascript
map.on('load', () => {
  // Add store data as source
  map.addSource('stores', {
    type: 'geojson',
    data: stores
  });

  // Add custom marker image
  map.loadImage('/marker-icon.png', (error, image) => {
    if (error) throw error;
    map.addImage('custom-marker', image);

    // Add symbol layer
    map.addLayer({
      id: 'stores-layer',
      type: 'symbol',
      source: 'stores',
      layout: {
        'icon-image': 'custom-marker',
        'icon-size': 0.8,
        'icon-allow-overlap': true,
        'text-field': ['get', 'name'],
        'text-font': ['Open Sans Bold', 'Arial Unicode MS Bold'],
        'text-offset': [0, 1.5],
        'text-anchor': 'top',
        'text-size': 12
      }
    });
  });

  // Handle marker clicks using Interactions API (recommended)
  map.addInteraction('store-click', {
    type: 'click',
    target: { layerId: 'stores-layer' },
    handler: (e) => {
      const store = e.feature;
      flyToStore(store);
      createPopup(store);
    }
  });

  // Or using traditional event listener:
  // map.on('click', 'stores-layer', (e) => {
  //   const store = e.features[0];
  //   flyToStore(store);
  //   createPopup(store);
  // });

  // Change cursor on hover
  map.on('mouseenter', 'stores-layer', () => {
    map.getCanvas().style.cursor = 'pointer';
  });

  map.on('mouseleave', 'stores-layer', () => {
    map.getCanvas().style.cursor = '';
  });
});
```

### Step 3: Build Interactive Location List

```javascript
function buildLocationList(stores) {
  const listingContainer = document.getElementById('listings');

  stores.features.forEach((store, index) => {
    const listing = listingContainer.appendChild(document.createElement('div'));
    listing.id = `listing-${store.properties.id}`;
    listing.className = 'listing';

    const link = listing.appendChild(document.createElement('a'));
    link.href = '#';
    link.className = 'title';
    link.id = `link-${store.properties.id}`;
    link.innerHTML = store.properties.name;

    const details = listing.appendChild(document.createElement('div'));
    details.innerHTML = `
      <p>${store.properties.address}</p>
      <p>${store.properties.phone || ''}</p>
    `;

    // Handle listing click
    link.addEventListener('click', (e) => {
      e.preventDefault();
      flyToStore(store);
      createPopup(store);
      highlightListing(store.properties.id);
    });
  });
}

function flyToStore(store) {
  map.flyTo({
    center: store.geometry.coordinates,
    zoom: 15,
    duration: 1000
  });
}

function createPopup(store) {
  const popups = document.getElementsByClassName('mapboxgl-popup');
  // Remove existing popups
  if (popups[0]) popups[0].remove();

  new mapboxgl.Popup({ closeOnClick: true })
    .setLngLat(store.geometry.coordinates)
    .setHTML(
      `<h3>${store.properties.name}</h3>
       <p>${store.properties.address}</p>
       <p>${store.properties.phone}</p>
       ${store.properties.website ? `<a href="${store.properties.website}" target="_blank">Visit Website</a>` : ''}`
    )
    .addTo(map);
}

// IMPORTANT: highlightListing MUST include scrollIntoView — without it,
// selecting a marker on the map won't scroll the sidebar to the listing.
function highlightListing(id) {
  // Remove existing highlights
  const activeItem = document.getElementsByClassName('active');
  if (activeItem[0]) {
    activeItem[0].classList.remove('active');
  }

  // Add highlight to selected listing
  const listing = document.getElementById(`listing-${id}`);
  listing.classList.add('active');

  // Scroll the selected listing into view (critical UX requirement)
  listing.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}

// Build the list on load
map.on('load', () => {
  buildLocationList(stores);
});
```

## Reference Files

Load these references for additional patterns as needed:

| Reference                 | File                                   | Contents                                                         |
| ------------------------- | -------------------------------------- | ---------------------------------------------------------------- |
| HTML Markers & Clustering | `references/markers.md`                | HTML Markers (< 100 locations), Clustering (> 1000 locations)    |
| Search & Filter           | `references/search-filter.md`          | Text search, category filter                                     |
| Geolocation & Directions  | `references/geolocation-directions.md` | User location, distance calculation, route directions            |
| Styling & Layout          | `references/styling-layout.md`         | Full HTML/CSS layout, custom marker CSS                          |
| Performance & A11y        | `references/optimization-a11y.md`      | Debounced search, data management, error handling, accessibility |
| Variations & React        | `references/variations-react.md`       | Mobile-first, fullscreen, map-only, React implementation         |

## Resources

- [Turf.js](https://turfjs.org/) - Spatial analysis library (recommended for distance calculations)
- [Mapbox GL JS API](https://docs.mapbox.com/mapbox-gl-js/)
- [Interactions API Guide](https://docs.mapbox.com/mapbox-gl-js/guides/user-interactions/interactions/)
- [GeoJSON Specification](https://geojson.org/)
- [Directions API](https://docs.mapbox.com/api/navigation/directions/)
- [Store Locator Tutorial](https://docs.mapbox.com/help/tutorials/building-a-store-locator/)

<!-- chapter:end slug=mapbox-store-locator-patterns -->

---

<!-- chapter:begin slug=mapbox-style-patterns position=15 -->

## 15. mapbox-style-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-style-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-style-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (9), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/evals/evals.json
  - `references/common-modifications.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/common-modifications.md
  - `references/dark-mode.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/dark-mode.md
  - `references/data-viz-base.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/data-viz-base.md
  - `references/delivery-logistics.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/delivery-logistics.md
  - `references/expressions-clustering.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/expressions-clustering.md
  - `references/navigation.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/navigation.md
  - `references/real-estate.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-patterns/references/real-estate.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-style-patterns
description: Common style patterns, layer configurations, and recipes for typical mapping scenarios including restaurant finders, real estate, data visualization, navigation, delivery/logistics, and more. Use when implementing specific map use cases or looking for proven style patterns.
---

# Mapbox Style Patterns Skill

This skill provides battle-tested style patterns and layer configurations for common mapping scenarios.

## Pattern Library

### Pattern 1: Restaurant/POI Finder

**Use case:** Consumer app showing restaurants, cafes, bars, or other points of interest

**Visual requirements:**

- POIs must be immediately visible
- Street context for navigation
- Neutral background (photos/content overlay)
- Mobile-optimized

**Recommended layers:**

```json
{
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {
        "background-color": "#f5f5f5"
      }
    },
    {
      "id": "water",
      "type": "fill",
      "source": "mapbox-streets",
      "source-layer": "water",
      "paint": {
        "fill-color": "#d4e4f7",
        "fill-opacity": 0.6
      }
    },
    {
      "id": "landuse-parks",
      "type": "fill",
      "source": "mapbox-streets",
      "source-layer": "landuse",
      "filter": ["==", "class", "park"],
      "paint": {
        "fill-color": "#e8f5e8",
        "fill-opacity": 0.5
      }
    },
    {
      "id": "roads-minor",
      "type": "line",
      "source": "mapbox-streets",
      "source-layer": "road",
      "filter": ["in", "class", "street", "street_limited"],
      "paint": {
        "line-color": "#e0e0e0",
        "line-width": {
          "base": 1.5,
          "stops": [
            [12, 0.5],
            [15, 2],
            [18, 6]
          ]
        }
      }
    },
    {
      "id": "roads-major",
      "type": "line",
      "source": "mapbox-streets",
      "source-layer": "road",
      "filter": ["in", "class", "primary", "secondary", "tertiary"],
      "paint": {
        "line-color": "#ffffff",
        "line-width": {
          "base": 1.5,
          "stops": [
            [10, 1],
            [15, 4],
            [18, 12]
          ]
        }
      }
    },
    {
      "id": "restaurant-markers",
      "type": "symbol",
      "source": "restaurants",
      "layout": {
        "icon-image": "restaurant-15",
        "icon-size": 1.5,
        "icon-allow-overlap": false,
        "text-field": ["get", "name"],
        "text-offset": [0, 1.5],
        "text-size": 12,
        "text-allow-overlap": false
      },
      "paint": {
        "icon-color": "#FF6B35",
        "text-color": "#333333",
        "text-halo-color": "#ffffff",
        "text-halo-width": 2
      }
    }
  ]
}
```

**Key features:**

- Desaturated base map (doesn't compete with photos)
- High-contrast markers (#FF6B35 orange stands out)
- Clear road network (white on light gray)
- Parks visible but subtle
- Text halos for readability

## Pattern Selection Guide

### Decision Tree

**Question 1: What is the primary content?**

- User-generated markers/pins -> **POI Finder Pattern**
- Property data/boundaries -> **Real Estate Pattern**
- Statistical/analytical data -> **Data Visualization Pattern**
- Routes/directions -> **Navigation Pattern**
- Real-time tracking/delivery zones -> **Delivery/Logistics Pattern** (customer markers should include a pulse animation via second circle layer + requestAnimationFrame + setPaintProperty; see references/delivery-logistics.md)

**Question 2: What is the viewing environment?**

- Daytime/office -> Light theme
- Night/dark environment -> **Dark Mode Pattern**
- Variable -> Provide theme toggle

**Question 3: What is the user's primary action?**

- Browse/explore -> Focus on POIs, rich detail
- Navigate -> Focus on roads, route visibility
- Track delivery/logistics -> Real-time updates, zones, status
- Analyze data -> Minimize base map, maximize data
- Select location -> Clear boundaries, context

**Question 4: What is the platform?**

- Mobile -> Simplified, larger touch targets, less detail
- Desktop -> Can include more detail and complexity
- Both -> Design mobile-first, enhance for desktop

## Layer Optimization Patterns

### Performance Pattern: Simplified by Zoom

```json
{
  "id": "roads",
  "type": "line",
  "source": "mapbox-streets",
  "source-layer": "road",
  "filter": [
    "step",
    ["zoom"],
    ["in", "class", "motorway", "trunk"],
    8,
    ["in", "class", "motorway", "trunk", "primary"],
    12,
    ["in", "class", "motorway", "trunk", "primary", "secondary"],
    14,
    true
  ],
  "paint": {
    "line-width": {
      "base": 1.5,
      "stops": [
        [4, 0.5],
        [10, 1],
        [15, 4],
        [18, 12]
      ]
    }
  }
}
```

## Reference Files

Additional patterns and configurations are available in the `references/` directory. Load the relevant file when a specific pattern is needed.

| File                                                                         | Contents                                                                                         |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| [references/real-estate.md](references/real-estate.md)                       | Pattern 2: Real Estate Map -- property boundaries, price color-coding, amenity markers           |
| [references/data-viz-base.md](references/data-viz-base.md)                   | Pattern 3: Data Visualization Base Map -- minimal grayscale base for choropleth/heatmap overlays |
| [references/navigation.md](references/navigation.md)                         | Pattern 4: Navigation/Routing Map -- route display, user location, turn arrows                   |
| [references/dark-mode.md](references/dark-mode.md)                           | Pattern 5: Dark Mode / Night Theme -- near-black background, reduced brightness                  |
| [references/delivery-logistics.md](references/delivery-logistics.md)         | Pattern 6: Delivery/Logistics Map -- real-time tracking, zones, driver markers, ETA badges       |
| [references/expressions-clustering.md](references/expressions-clustering.md) | Data-driven expression patterns + clustering for dense POIs                                      |
| [references/common-modifications.md](references/common-modifications.md)     | 3D Buildings, Terrain/Hillshade, Custom Markers                                                  |

**Loading instructions:** Read the reference file that matches the user's use case. For example, if implementing a delivery tracking map, load `references/delivery-logistics.md`.

## Testing Patterns

### Visual Regression Checklist

- [ ] Test at zoom levels: 4, 8, 12, 16, 20
- [ ] Verify on mobile (375px width)
- [ ] Verify on desktop (1920px width)
- [ ] Test with dense data
- [ ] Test with sparse data
- [ ] Check label collision
- [ ] Verify color contrast (WCAG)
- [ ] Test loading performance

## When to Use This Skill

Invoke this skill when:

- Starting a new map style for a specific use case
- Looking for layer configuration examples
- Implementing common mapping patterns
- Optimizing existing styles
- Need proven recipes for typical scenarios
- Debugging style issues
- Learning Mapbox style best practices

<!-- chapter:end slug=mapbox-style-patterns -->

---

<!-- chapter:begin slug=mapbox-style-quality position=16 -->

## 16. mapbox-style-quality

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-style-quality/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-quality/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-style-quality.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (5), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-quality/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-quality/evals/evals.json
  - `references/ci-integration.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-quality/references/ci-integration.md
  - `references/comparison.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-quality/references/comparison.md
  - `references/optimization.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-style-quality/references/optimization.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-style-quality
description: Expert guidance on validating, optimizing, and ensuring quality of Mapbox styles through validation, accessibility checks, and optimization. Use when preparing styles for production, debugging issues, or ensuring map quality standards.
---

# Mapbox Style Quality Skill

This skill provides expert guidance on ensuring Mapbox style quality through validation, accessibility, and optimization tools.

## When to Use Quality Tools

### Pre-Production Checklist

Before deploying any Mapbox style to production:

1. **Validate all expressions** - Catch syntax errors before runtime
2. **Check color contrast** - Ensure text is readable (WCAG compliance)
3. **Validate GeoJSON sources** - Ensure data integrity
4. **Optimize style** - Reduce file size and improve performance
5. **Compare versions** - Understand what changed
6. **Remove empty layers** - Delete layers with no visible paint properties as a final cleanup step
7. **Simplify redundant boolean expressions** - Clean up filters with unnecessary boolean logic (e.g., `["all", expr]` → `expr`, `["any", false, expr]` → `expr`)

### During Development

**When adding GeoJSON data:**

- Always validate external GeoJSON with `validate_geojson_tool` before using as a source

**When writing expressions:**

- Validate expressions with `validate_expression_tool` as you write them
- Catch type mismatches early (e.g., using string operator on number)
- Verify operator availability in your Mapbox GL JS version
- Test expressions with expected data types

**When styling text/labels:**

- Check foreground/background contrast with `check_color_contrast_tool`
- Aim for WCAG AA minimum (4.5:1 for normal text, 3:1 for large text)
- Use AAA standard (7:1 for normal text) for better accessibility
- Consider different background scenarios (map tiles, overlays)

### Before Committing Changes

**Compare style versions:**

- Use `compare_styles_tool` to generate a diff report
- Review all layer changes, source modifications, and expression updates
- Understand the impact of your changes
- Document significant changes in commit messages

### Before Deployment

**Optimize the style:**

- Run `optimize_style_tool` to reduce file size
- Remove unused sources that reference deleted layers
- Eliminate duplicate layers with identical properties
- Simplify redundant boolean expressions in filters (e.g., collapse `["all", expr]` to `expr`, remove tautological conditions)
- Remove empty layers (layers with no visible paint properties) as a final cleanup step

## Validation Best Practices

### GeoJSON Validation

**Always validate when:**

- Loading GeoJSON from user uploads
- Fetching GeoJSON from external APIs
- Processing GeoJSON from third-party sources
- Converting between data formats

**Common GeoJSON errors:**

- Invalid coordinate ranges (longitude > 180 or < -180)
- Unclosed polygon rings (first and last coordinates must match)
- Wrong coordinate order (should be [longitude, latitude], not [latitude, longitude])
- Missing required properties (type, coordinates, geometry)
- Invalid geometry types or nesting

**Example workflow:**

```
1. Receive GeoJSON data
2. Validate with validate_geojson_tool
3. If valid: Add as source to style
4. If invalid: Fix errors, re-validate
```

### Expression Validation

**Validate expressions for:**

- Filter conditions (`filter` property on layers)
- Data-driven styling (`paint` and `layout` properties)
- Feature state expressions
- Dynamic property calculations

**Common expression errors:**

- Type mismatches (string operators on numbers)
- Invalid operator names or wrong syntax
- Wrong number of arguments for operators
- Nested expression errors
- Using unavailable operators for your GL JS version

**Prevention strategies:**

- Validate as you write expressions, not at runtime
- Test expressions with representative data
- Use type checking (expectedType parameter)
- Validate in context (layer, filter, paint, layout)

### Accessibility Validation

**WCAG Levels:**

- **AA** (minimum): 4.5:1 for normal text, 3:1 for large text
- **AAA** (enhanced): 7:1 for normal text, 4.5:1 for large text

**Text size categories:**

- **Normal**: < 18pt or < 14pt bold
- **Large**: ≥ 18pt or ≥ 14pt bold

**Common scenarios to check:**

- Text labels on map tiles
- POI labels with background colors
- Custom markers with text
- UI overlays on maps
- Legend text and symbols
- Attribution text

**Testing strategy:**

- Test against both light and dark map tiles
- Consider overlay backgrounds (popups, modals)
- Test in different lighting conditions (mobile outdoor use)
- Verify contrast at different zoom levels

## Quality Workflow Examples

### Basic Quality Check

```
1. Validate expressions in style
2. Check color contrast for text layers
3. Optimize if needed
```

### Full Pre-Production Workflow

```
1. Validate all GeoJSON sources
2. Validate all expressions (filters, paint, layout)
3. Check color contrast for all text layers
4. Compare with previous production version
5. Optimize style
6. Test optimized style
7. Deploy
```

### Troubleshooting Workflow

```
1. Compare working vs. broken style
2. Identify differences
3. Validate suspicious expressions
4. Check GeoJSON data if source-related
5. Verify color contrast if visibility issue
```

## Common Issues and Solutions

### Runtime Expression Errors

**Problem:** Map throws expression errors at runtime
**Solution:** Validate expressions with `validate_expression_tool` during development
**Prevention:** Add expression validation to pre-commit hooks or CI/CD

### Poor Text Readability

**Problem:** Text labels are hard to read on map
**Solution:** Check contrast with `check_color_contrast_tool`, adjust colors to meet WCAG AA
**Prevention:** Test text on both light and dark backgrounds, check at different zoom levels

### Large Style File Size

**Problem:** Style takes long to load or transfer
**Solution:** Run `optimize_style_tool` to remove redundancies and simplify
**Prevention:** Regularly optimize during development, remove unused sources immediately

### Invalid GeoJSON Source

**Problem:** GeoJSON source fails to load or render
**Solution:** Validate with `validate_geojson_tool`, fix coordinate issues, verify structure
**Prevention:** Validate all external GeoJSON before adding to style

### Unexpected Style Changes

**Problem:** Style changed but unsure what modified
**Solution:** Use `compare_styles_tool` to generate diff report
**Prevention:** Compare before/after for all significant changes, document modifications

## Tool Quick Reference

| Tool                        | Use When               | Output                     |
| --------------------------- | ---------------------- | -------------------------- |
| `validate_geojson_tool`     | Adding GeoJSON sources | Valid/invalid + error list |
| `validate_expression_tool`  | Writing expressions    | Valid/invalid + error list |
| `check_color_contrast_tool` | Styling text labels    | Passes/fails + WCAG levels |
| `compare_styles_tool`       | Reviewing changes      | Diff report with paths     |
| `optimize_style_tool`       | Before deployment      | Optimized style + savings  |

## Reference Files

For detailed guidance on specific topics, load the relevant reference:

- **`references/optimization.md`** — Optimization types, strategies, recommended order, and maintenance best practices
- **`references/comparison.md`** — Style comparison workflows, ignoreMetadata usage, and refactoring workflow
- **`references/ci-integration.md`** — Git pre-commit hooks, CI/CD pipeline steps, and code review checklist

> **Load instruction:** Read the reference file when the user needs in-depth guidance on that topic.

## Additional Resources

- [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/)
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
- [GeoJSON Specification (RFC 7946)](https://tools.ietf.org/html/rfc7946)
- [Mapbox Expression Reference](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/)

<!-- chapter:end slug=mapbox-style-quality -->

---

<!-- chapter:begin slug=mapbox-token-security position=17 -->

## 17. mapbox-token-security

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-token-security/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-token-security/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-token-security.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (5), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-token-security/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-token-security/evals/evals.json
  - `references/incident-response.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-token-security/references/incident-response.md
  - `references/rotation-monitoring.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-token-security/references/rotation-monitoring.md
  - `references/token-management.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-token-security/references/token-management.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-token-security
description: Security best practices for Mapbox access tokens, including scope management, URL restrictions, rotation strategies, and protecting sensitive data. Use when creating, managing, or advising on Mapbox token security.
---

# Mapbox Token Security Skill

This skill provides security expertise for managing Mapbox access tokens safely and effectively.

## Token Types and When to Use Them

### Public Tokens (pk.\*)

**Characteristics:**

- Can be safely exposed in client-side code
- Limited to specific public scopes only
- Can have URL restrictions
- Cannot access sensitive APIs

**When to use:**

- Client-side web applications
- Mobile apps
- Public-facing demos
- Embedded maps on websites

**Allowed scopes:**

- `styles:tiles` - Display style tiles (raster)
- `styles:read` - Read style specifications
- `fonts:read` - Access Mapbox fonts
- `datasets:read` - Read dataset data
- `vision:read` - Vision API access

### Secret Tokens (sk.\*)

**Characteristics:**

- **NEVER expose in client-side code**
- Full API access with any scopes
- Server-side use only
- Can create/manage other tokens

**When to use:**

- Server-side applications
- Backend services
- CI/CD pipelines
- Administrative tasks
- Token management

**Common scopes:**

- `styles:write` - Create/modify styles
- `styles:list` - List all styles
- `tokens:read` - View token information
- `tokens:write` - Create/modify tokens
- User feedback management scopes

### Temporary Tokens (tk.\*)

**Characteristics:**

- Short-lived (max 1 hour)
- Created by secret tokens
- Single-purpose use
- Automatically expire

**When to use:**

- One-time operations
- Temporary delegated access
- Short-lived demos
- Security-conscious workflows

## Scope Management Best Practices

### Principle of Least Privilege

**Always grant the minimum scopes needed:**

❌ **Bad:**

```javascript
// Overly permissive - don't do this
{
  scopes: ['styles:read', 'styles:write', 'styles:list', 'styles:delete', 'tokens:read', 'tokens:write'];
}
```

✅ **Good:**

```javascript
// Only what's needed for displaying a map
{
  scopes: ['styles:read', 'fonts:read'];
}
// Add 'styles:tiles' if your map uses raster tile sources
{
  scopes: ['styles:read', 'fonts:read', 'styles:tiles'];
}
```

### Scope Combinations by Use Case

**Public Map Display (client-side):**

```json
{
  "scopes": ["styles:read", "fonts:read", "styles:tiles"],
  "note": "Public token for map display",
  "allowedUrls": ["https://myapp.com/*"]
}
```

**Style Management (server-side):**

```json
{
  "scopes": ["styles:read", "styles:write", "styles:list"],
  "note": "Backend style management - SECRET TOKEN"
}
```

**Token Administration (server-side):**

```json
{
  "scopes": ["tokens:read", "tokens:write"],
  "note": "Token management only - SECRET TOKEN"
}
```

**Read-Only Access:**

```json
{
  "scopes": ["styles:list", "styles:read", "tokens:read"],
  "note": "Auditing/monitoring - SECRET TOKEN"
}
```

## URL Restrictions

### Why URL Restrictions Matter

URL restrictions limit where a public token can be used, preventing unauthorized usage if the token is exposed.

### Effective URL Patterns

✅ **Recommended patterns:**

```
https://myapp.com/*           # Production domain
https://*.myapp.com/*         # All subdomains
https://staging.myapp.com/*   # Staging environment
http://localhost:*            # Local development
```

❌ **Avoid these:**

```
*                             # No restriction (insecure)
http://*                      # Any HTTP site (insecure)
*.com/*                       # Too broad
```

### Multiple Environment Strategy

Create separate tokens for each environment:

```javascript
// Production
{
  note: "Production - myapp.com",
  scopes: ["styles:read", "fonts:read"],
  allowedUrls: ["https://myapp.com/*", "https://www.myapp.com/*"]
}

// Staging
{
  note: "Staging - staging.myapp.com",
  scopes: ["styles:read", "fonts:read"],
  allowedUrls: ["https://staging.myapp.com/*"]
}

// Development
{
  note: "Development - localhost",
  scopes: ["styles:read", "fonts:read"],
  allowedUrls: ["http://localhost:*", "http://127.0.0.1:*"]
}
```

## Token Storage and Handling

### Server-Side (Secret Tokens)

✅ **DO:**

- Store in environment variables
- Use secret management services (AWS Secrets Manager, HashiCorp Vault)
- Encrypt at rest
- Limit access via IAM policies
- Log token usage

❌ **DON'T:**

- Hardcode in source code
- Commit to version control
- Store in plaintext configuration files
- Share via email or Slack
- Reuse across multiple services

**Example: Secure Environment Variable:**

```bash
# .env (NEVER commit this file)
MAPBOX_SECRET_TOKEN=sk.ey...

# .gitignore (ALWAYS include .env)
.env
.env.local
.env.*.local
```

### Client-Side (Public Tokens)

✅ **DO:**

- Use public tokens only
- Apply URL restrictions
- Use different tokens per app
- Rotate periodically
- Monitor usage

❌ **DON'T:**

- Expose secret tokens
- Use tokens without URL restrictions
- Share tokens between unrelated apps
- Use tokens with excessive scopes

**Example: Safe Client Usage (Vite):**

> **Note:** This example uses **Vite**. For Next.js, CRA, Angular, or a plain `window.MAPBOX_ACCESS_TOKEN` / CDN setup, see [Token Management](references/token-management.md). Do not chain `import.meta.env` and `process.env` in one expression — the unused path throws `ReferenceError` in the browser.

```javascript
// Public token with URL restrictions - SAFE (Vite)
const mapboxToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;

// Guard BEFORE constructing the map — missing tokens otherwise yield a silent blank map
if (!mapboxToken || mapboxToken === 'YOUR_MAPBOX_ACCESS_TOKEN') {
  throw new Error('Missing VITE_MAPBOX_ACCESS_TOKEN — set it in env before creating the map');
}

mapboxgl.accessToken = mapboxToken;
```

### Agent anti-pattern: skip the token guard

Agents often assign `mapboxgl.accessToken` and call `new mapboxgl.Map(...)` with no check. That fails closed as a blank canvas with no UI error.

**Always:**

1. Resolve the token from the env pattern for your bundler (never hardcode a real `pk.` in source)
2. Validate it is present and not a placeholder
3. Only then set `accessToken` and construct the map

## Security Checklist

**Token Creation:**

- [ ] Use public tokens for client-side, secret for server-side
- [ ] Apply principle of least privilege for scopes
- [ ] Add URL restrictions to public tokens
- [ ] Use descriptive names/notes for token identification
- [ ] Document intended use and environment

**Token Management:**

- [ ] Store secret tokens in environment variables or secret managers
- [ ] Never commit tokens to version control
- [ ] Rotate tokens every 90 days (or per policy)
- [ ] Remove unused tokens promptly
- [ ] Separate tokens by environment (dev/staging/prod)
- [ ] Guard missing tokens in client code before `new mapboxgl.Map`

**Monitoring:**

- [ ] Track token usage patterns
- [ ] Set up alerts for unusual activity
- [ ] Regular security audits (monthly)
- [ ] Review team access quarterly
- [ ] Scan repositories for exposed tokens

**Incident Response:**

- [ ] Documented revocation procedure
- [ ] Emergency contact list
- [ ] Rotation process documented
- [ ] Post-incident review template
- [ ] Team training on security procedures

## Reference Files

For detailed guidance on specific topics, load these references as needed:

- **`references/token-management.md`** — Bundler-specific env var names and access patterns (Vite / Next / CRA / Angular / CDN). Load when: wiring tokens in a different framework than the Vite example above.
- **`references/rotation-monitoring.md`** — Token rotation strategies (zero-downtime + emergency), monitoring metrics, alerting rules, and monthly/quarterly audit checklists. Load when: implementing rotation, setting up monitoring, or conducting audits.
- **`references/incident-response.md`** — Step-by-step incident response plan and common security mistakes with code examples. Load when: responding to a token compromise, reviewing code for security issues, or training on anti-patterns.

## When to Use This Skill

Invoke this skill when:

- Creating new tokens
- Deciding between public vs secret tokens
- Setting up token restrictions
- Implementing token rotation
- Investigating security incidents
- Conducting security audits
- Training team on token security
- Reviewing code for token exposure

<!-- chapter:end slug=mapbox-token-security -->

---

<!-- chapter:begin slug=mapbox-web-integration-patterns position=18 -->

## 18. mapbox-web-integration-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-web-integration-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-web-integration-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (10), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/evals/evals.json
  - `references/angular.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/angular.md
  - `references/common-mistakes.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/common-mistakes.md
  - `references/nextjs.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/nextjs.md
  - `references/svelte.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/svelte.md
  - `references/token-management.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/token-management.md
  - `references/vanilla.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/vanilla.md
  - `references/vue.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/vue.md
  - `references/web-components.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-integration-patterns/references/web-components.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

---
name: mapbox-web-integration-patterns
description: Official integration patterns for Mapbox GL JS across popular web frameworks (React, Vue, Svelte, Angular). Covers setup, lifecycle management, token handling, search integration, and common pitfalls. Based on Mapbox's create-web-app scaffolding tool.
---

# Mapbox Integration Patterns Skill

This skill provides official patterns for integrating Mapbox GL JS into web applications using React, Vue, Svelte, Angular, and vanilla JavaScript. These patterns are based on Mapbox's `create-web-app` scaffolding tool and represent production-ready best practices.

## Version Requirements

### Mapbox GL JS

**Recommended:** v3.x (latest)

- **Minimum:** v3.0.0
- **Why v3.x:** Modern API, improved performance, active development
- **v2.x:** Legacy; no longer actively developed (see migration notes below)

**Installing via npm (recommended for production):**

```bash
npm install mapbox-gl@^3.0.0    # Installs latest v3.x
```

**CDN (for prototyping only):**

```html
<!-- Replace VERSION with latest v3.x from https://docs.mapbox.com/mapbox-gl-js/ -->
<script src="https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.css" rel="stylesheet" />
```

### Framework Requirements

**React:** GL JS works with React 16.8+ (requires hooks). `create-web-app` scaffolds with React 19.x.
**Vue:** GL JS works with Vue 2.x+ (Vue 3 Composition API recommended).
**Svelte:** GL JS works with any Svelte version. `create-web-app` scaffolds with Svelte 5.x.
**Angular:** GL JS works with Angular 2+. `create-web-app` scaffolds with Angular 19.x.
**Next.js:** Minimum 13.x (App Router), Pages Router 12.x+.

### Mapbox Search JS

```bash
npm install @mapbox/search-js-react@^1.0.0      # React
npm install @mapbox/search-js-web@^1.0.0        # Other frameworks
```

### Version Migration Notes (v2.x to v3.x)

- WebGL 2 now required
- `optimizeForTerrain` option removed
- Improved TypeScript types, better tree-shaking support
- No breaking changes to core initialization patterns

**Token patterns (work in v2.x and v3.x):**

```javascript
const token = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN; // Use env vars in production

// Global token (works since v1.x)
mapboxgl.accessToken = token;
const map = new mapboxgl.Map({ container: '...' });

// Per-map token (preferred for multi-map setups)
const map = new mapboxgl.Map({
  accessToken: token,
  container: '...'
});
```

## Core Principles

**Every Mapbox GL JS integration must:**

1. Initialize the map in the correct lifecycle hook
2. Store map instance in component state (not recreate on every render)
3. **Always call `map.remove()` on cleanup** to prevent memory leaks
4. Handle token management securely (environment variables)
5. Import CSS: `import 'mapbox-gl/dist/mapbox-gl.css'`

## React Integration (Primary Pattern)

**Pattern: useRef + useEffect with cleanup**

> **Note:** These examples use **Vite** (the bundler used in `create-web-app`). If using Create React App, replace `import.meta.env.VITE_MAPBOX_ACCESS_TOKEN` with `process.env.REACT_APP_MAPBOX_TOKEN`. See [Token Management Patterns](references/token-management.md) for other bundlers.

```jsx
import { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

function MapComponent() {
  const mapRef = useRef(null); // Store map instance
  const mapContainerRef = useRef(null); // Store DOM reference

  useEffect(() => {
    mapboxgl.accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;

    mapRef.current = new mapboxgl.Map({
      container: mapContainerRef.current,
      center: [-71.05953, 42.3629],
      zoom: 13
    });

    // CRITICAL: Cleanup to prevent memory leaks
    return () => {
      mapRef.current.remove();
    };
  }, []); // Empty dependency array = run once on mount

  return <div ref={mapContainerRef} style={{ height: '100vh' }} />;
}
```

**Key points:**

- Use `useRef` for both map instance and container
- Initialize in `useEffect` with empty deps `[]`
- **Always return cleanup function** that calls `map.remove()`
- Never initialize map in render (causes infinite loops)

### React + Search JS

```jsx
import { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import { SearchBox } from '@mapbox/search-js-react';
import 'mapbox-gl/dist/mapbox-gl.css';

const accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
const center = [-71.05953, 42.3629];

function MapWithSearch() {
  const mapRef = useRef(null);
  const mapContainerRef = useRef(null);
  const [inputValue, setInputValue] = useState('');

  useEffect(() => {
    mapboxgl.accessToken = accessToken;

    mapRef.current = new mapboxgl.Map({
      container: mapContainerRef.current,
      center: center,
      zoom: 13
    });

    return () => {
      mapRef.current.remove();
    };
  }, []);

  return (
    <>
      <div
        style={{
          margin: '10px 10px 0 0',
          width: 300,
          right: 0,
          top: 0,
          position: 'absolute',
          zIndex: 10
        }}
      >
        <SearchBox
          accessToken={accessToken}
          map={mapRef.current}
          mapboxgl={mapboxgl}
          value={inputValue}
          proximity={center}
          onChange={(d) => setInputValue(d)}
          marker
        />
      </div>
      <div ref={mapContainerRef} style={{ height: '100vh' }} />
    </>
  );
}
```

## Search JS Integration Summary

**Install:**

```bash
npm install @mapbox/search-js-react      # React
npm install @mapbox/search-js-web        # Vanilla/Vue/Svelte
```

Both packages include `@mapbox/search-js-core` as a dependency. Only install `-core` directly if building a custom search UI.

**Key configuration options:**

- `accessToken`: Your Mapbox public token
- `map`: Map instance (must be initialized first)
- `mapboxgl`: The mapboxgl library reference
- `proximity`: `[lng, lat]` to bias results geographically
- `marker`: Boolean to show/hide result marker
- `placeholder`: Search box placeholder text

### Positioning Search Box

**Absolute positioning (overlay):**

```jsx
<div
  style={{
    position: 'absolute',
    top: 10,
    right: 10,
    zIndex: 10,
    width: 300
  }}
>
  <SearchBox {...props} />
</div>
```

**Common positions:**

- Top-right: `top: 10px, right: 10px`
- Top-left: `top: 10px, left: 10px`
- Bottom-left: `bottom: 10px, left: 10px`

## Common Mistakes (Critical)

### Mistake 1: Forgetting to call map.remove()

```javascript
// BAD - Memory leak!
useEffect(() => {
  const map = new mapboxgl.Map({ ... })
  // No cleanup function
}, [])

// GOOD - Proper cleanup
useEffect(() => {
  const map = new mapboxgl.Map({ ... })
  return () => map.remove()  // Cleanup
}, [])
```

**Why:** Every Map instance creates WebGL contexts, event listeners, and DOM nodes. Without cleanup, these accumulate and cause memory leaks.

### Mistake 2: Initializing map in render

```javascript
// BAD - Infinite loop in React!
function MapComponent() {
  const map = new mapboxgl.Map({ ... })  // Runs on every render
  return <div />
}

// GOOD - Initialize in effect
function MapComponent() {
  useEffect(() => {
    const map = new mapboxgl.Map({ ... })
  }, [])
  return <div />
}
```

**Why:** React components re-render frequently. Creating a new map on every render causes infinite loops and crashes.

### Mistake 3: Not storing map instance properly

```javascript
// BAD - map variable lost between renders
function MapComponent() {
  useEffect(() => {
    let map = new mapboxgl.Map({ ... })
    // map variable is not accessible later
  }, [])
}

// GOOD - Store in useRef
function MapComponent() {
  const mapRef = useRef()
  useEffect(() => {
    mapRef.current = new mapboxgl.Map({ ... })
    // mapRef.current accessible throughout component
  }, [])
}
```

**Why:** You need to access the map instance for operations like adding layers, markers, or calling `remove()`.

### Mistake 4: Storing map instance in Vue's data() (Vue-specific)

```javascript
// BAD - Vue's reactivity wraps data() objects in a Proxy, breaking mapbox-gl internals!
export default {
  data() {
    return {
      map: null  // Will be wrapped in a Proxy
    }
  },
  mounted() {
    this.map = new mapboxgl.Map({ ... })  // Proxy breaks GL internals
  }
}

// GOOD - Assign map as a plain instance property, not in data()
export default {
  mounted() {
    this.map = new mapboxgl.Map({
      container: this.$refs.mapContainer,
      center: [-71.05953, 42.3629],
      zoom: 13
    })
  },
  unmounted() {
    this.map?.remove()
  }
}
```

**Why:** In Vue (especially Vue 3), `data()` properties are wrapped in a `Proxy` for reactivity. Mapbox GL JS internally checks object identity and uses properties that don't survive proxy wrapping. Storing the map in `data()` causes subtle, hard-to-debug failures. Instead, assign the map instance directly as `this.map` in `mounted()` — properties assigned outside `data()` are not made reactive.

### Mistake 5: Silent style/tile failures (no error handler)

```javascript
// BAD — blank map when the token/style fails
const map = new mapboxgl.Map({ ... });

// GOOD — surface failures
map.on('error', (e) => {
  console.error(e.error || e);
  // optionally show an on-page error message
});
```

### Mistake 6: Broken deck.gl CDN via jsDelivr `+esm`

```html
<!-- BAD — often throws: does not provide export named 'makeBatchFromTable' -->
<script type="module">
  import { MapboxOverlay } from 'https://cdn.jsdelivr.net/npm/@deck.gl/mapbox@9.0.0/+esm';
</script>

<!-- GOOD — UMD bundle (or esm.sh) -->
<script src="https://unpkg.com/deck.gl@9.1.14/dist.min.js"></script>
<script>
  const { MapboxOverlay, ScatterplotLayer } = deck;
  map.addControl(
    new MapboxOverlay({
      interleaved: false,
      layers: [
        /* ... */
      ]
    })
  );
</script>
```

Use `MapboxOverlay` (Mapbox IControl), not a bare `Deck` as a map control.

### Mistake 7: Draw toolbar without `draw.create`

If you load `mapbox-gl-draw`, listen for `draw.create` (and update the UI from `draw.getAll()`). Half-deleted handlers that leave a dangling `});` crash the page.

### Mistake 8: Layers lost after `setStyle` (no `style.load` rebind)

`map.setStyle(...)` replaces the style tree. Custom sources/layers/handlers added earlier are wiped unless you re-attach them.

```javascript
function onStyleReady() {
  // re-add sources, layers, and interaction handlers here
}

map.on('style.load', onStyleReady);

document.querySelectorAll('[data-style]').forEach((btn) => {
  btn.addEventListener('click', () => {
    map.setStyle(btn.dataset.style);
    // do NOT only add layers on the first 'load' — wait for style.load after every switch
  });
});
```

**Agent anti-pattern:** style switcher buttons that call `setStyle` once with no `style.load` rebind. The first style works; every switch after looks broken.

## Reference Files

Load these for framework-specific patterns and additional details:

- `references/vue.md` — Vue Integration (mounted/unmounted lifecycle)
- `references/svelte.md` — Svelte Integration (onMount/onDestroy)
- `references/angular.md` — Angular Integration with SSR handling
- `references/vanilla.md` — Vanilla JS (Vite) + Vanilla JS (CDN)
- `references/web-components.md` — Web Components (basic + reactive + usage in React/Vue/Svelte)
- `references/nextjs.md` — Next.js App Router + Pages Router
- `references/common-mistakes.md` — Common Mistakes 4-7 + Testing Patterns
- `references/token-management.md` — Token Management per bundler + Style Configuration

## When to Use This Skill

Invoke this skill when:

- Setting up Mapbox GL JS in a new project
- Integrating Mapbox into a specific framework (React, Vue, Svelte, Angular, Next.js)
- Building framework-agnostic Web Components
- Creating reusable map components for component libraries
- Debugging map initialization issues
- Adding Mapbox Search functionality
- Implementing proper cleanup and lifecycle management
- Converting between frameworks (e.g., React to Vue)
- Reviewing code for Mapbox integration best practices

## Related Skills

- **mapbox-cartography**: Map design principles and styling
- **mapbox-token-security**: Token management and security
- **mapbox-style-patterns**: Common map style patterns

## Resources

- [Mapbox GL JS Documentation](https://docs.mapbox.com/mapbox-gl-js/)
- [Mapbox Search JS Documentation](https://docs.mapbox.com/mapbox-search-js/)
- [create-web-app GitHub](https://github.com/mapbox/create-web-app)

<!-- chapter:end slug=mapbox-web-integration-patterns -->

---

<!-- chapter:begin slug=mapbox-web-performance-patterns position=19 -->

## 19. mapbox-web-performance-patterns

- **Source:** https://github.com/mapbox/mapbox-agent-skills/blob/main/skills/mapbox-web-performance-patterns/SKILL.md
- **Raw:** https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/SKILL.md
- **Markdown:** https://skillsdocs.com/mapbox/mapbox-agent-skills/mapbox-web-performance-patterns.md
- **Licence:** MIT — https://spdx.org/licenses/MIT.html

Bundled files (7), referenced from this skill's directory:
  - `AGENTS.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/AGENTS.md
  - `evals/evals.json` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/evals/evals.json
  - `references/data-loading.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/references/data-loading.md
  - `references/interactions.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/references/interactions.md
  - `references/layers-styles.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/references/layers-styles.md
  - `references/memory.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/references/memory.md
  - `references/mobile.md` — https://raw.githubusercontent.com/mapbox/mapbox-agent-skills/main/skills/mapbox-web-performance-patterns/references/mobile.md

<!-- Verbatim upstream SKILL.md follows, YAML frontmatter included. -->

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

<!-- chapter:end slug=mapbox-web-performance-patterns -->
