← MapboxCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to Mapbox
Snapshot Sep 30, 2026 · 23:11 UTC · version 1.0.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"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.",
"included_files": [
{
"relative_path": "AGENTS.md",
"size_in_bytes": 7264
},
{
"relative_path": "evals/evals.json",
"size_in_bytes": 5659
},
{
"relative_path": "references/angular.md",
"size_in_bytes": 2073
},
{
"relative_path": "references/common-mistakes.md",
"size_in_bytes": 2791
},
{
"relative_path": "references/nextjs.md",
"size_in_bytes": 1382
},
{
"relative_path": "references/svelte.md",
"size_in_bytes": 895
},
{
"relative_path": "references/token-management.md",
"size_in_bytes": 2092
},
{
"relative_path": "references/vanilla.md",
"size_in_bytes": 3040
},
{
"relative_path": "references/vue.md",
"size_in_bytes": 987
},
{
"relative_path": "references/web-components.md",
"size_in_bytes": 6412
}
],
"name": "mapbox-web-integration-patterns",
"skill_md_contents": "---\nname: mapbox-web-integration-patterns\ndescription: 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.\n---\n\n# Mapbox Integration Patterns Skill\n\nThis 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.\n\n## Version Requirements\n\n### Mapbox GL JS\n\n**Recommended:** v3.x (latest)\n\n- **Minimum:** v3.0.0\n- **Why v3.x:** Modern API, improved performance, active development\n- **v2.x:** Legacy; no longer actively developed (see migration notes below)\n\n**Installing via npm (recommended for production):**\n\n```bash\nnpm install mapbox-gl@^3.0.0 # Installs latest v3.x\n```\n\n**CDN (for prototyping only):**\n\n```html\n<!-- Replace VERSION with latest v3.x from https://docs.mapbox.com/mapbox-gl-js/ -->\n<script src=\"https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.js\"></script>\n<link href=\"https://api.mapbox.com/mapbox-gl-js/vVERSION/mapbox-gl.css\" rel=\"stylesheet\" />\n```\n\n### Framework Requirements\n\n**React:** GL JS works with React 16.8+ (requires hooks). `create-web-app` scaffolds with React 19.x.\n**Vue:** GL JS works with Vue 2.x+ (Vue 3 Composition API recommended).\n**Svelte:** GL JS works with any Svelte version. `create-web-app` scaffolds with Svelte 5.x.\n**Angular:** GL JS works with Angular 2+. `create-web-app` scaffolds with Angular 19.x.\n**Next.js:** Minimum 13.x (App Router), Pages Router 12.x+.\n\n### Mapbox Search JS\n\n```bash\nnpm install @mapbox/search-js-react@^1.0.0 # React\nnpm install @mapbox/search-js-web@^1.0.0 # Other frameworks\n```\n\n### Version Migration Notes (v2.x to v3.x)\n\n- WebGL 2 now required\n- `optimizeForTerrain` option removed\n- Improved TypeScript types, better tree-shaking support\n- No breaking changes to core initialization patterns\n\n**Token patterns (work in v2.x and v3.x):**\n\n```javascript\nconst token = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN; // Use env vars in production\n\n// Global token (works since v1.x)\nmapboxgl.accessToken = token;\nconst map = new mapboxgl.Map({ container: '...' });\n\n// Per-map token (preferred for multi-map setups)\nconst map = new mapboxgl.Map({\n accessToken: token,\n container: '...'\n});\n```\n\n## Core Principles\n\n**Every Mapbox GL JS integration must:**\n\n1. Initialize the map in the correct lifecycle hook\n2. Store map instance in component state (not recreate on every render)\n3. **Always call `map.remove()` on cleanup** to prevent memory leaks\n4. Handle token management securely (environment variables)\n5. Import CSS: `import 'mapbox-gl/dist/mapbox-gl.css'`\n\n## React Integration (Primary Pattern)\n\n**Pattern: useRef + useEffect with cleanup**\n\n> **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.\n\n```jsx\nimport { useRef, useEffect } from 'react';\nimport mapboxgl from 'mapbox-gl';\nimport 'mapbox-gl/dist/mapbox-gl.css';\n\nfunction MapComponent() {\n const mapRef = useRef(null); // Store map instance\n const mapContainerRef = useRef(null); // Store DOM reference\n\n useEffect(() => {\n mapboxgl.accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;\n\n mapRef.current = new mapboxgl.Map({\n container: mapContainerRef.current,\n center: [-71.05953, 42.3629],\n zoom: 13\n });\n\n // CRITICAL: Cleanup to prevent memory leaks\n return () => {\n mapRef.current.remove();\n };\n }, []); // Empty dependency array = run once on mount\n\n return <div ref={mapContainerRef} style={{ height: '100vh' }} />;\n}\n```\n\n**Key points:**\n\n- Use `useRef` for both map instance and container\n- Initialize in `useEffect` with empty deps `[]`\n- **Always return cleanup function** that calls `map.remove()`\n- Never initialize map in render (causes infinite loops)\n\n### React + Search JS\n\n```jsx\nimport { useRef, useEffect, useState } from 'react';\nimport mapboxgl from 'mapbox-gl';\nimport { SearchBox } from '@mapbox/search-js-react';\nimport 'mapbox-gl/dist/mapbox-gl.css';\n\nconst accessToken = import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;\nconst center = [-71.05953, 42.3629];\n\nfunction MapWithSearch() {\n const mapRef = useRef(null);\n const mapContainerRef = useRef(null);\n const [inputValue, setInputValue] = useState('');\n\n useEffect(() => {\n mapboxgl.accessToken = accessToken;\n\n mapRef.current = new mapboxgl.Map({\n container: mapContainerRef.current,\n center: center,\n zoom: 13\n });\n\n return () => {\n mapRef.current.remove();\n };\n }, []);\n\n return (\n <>\n <div\n style={{\n margin: '10px 10px 0 0',\n width: 300,\n right: 0,\n top: 0,\n position: 'absolute',\n zIndex: 10\n }}\n >\n <SearchBox\n accessToken={accessToken}\n map={mapRef.current}\n mapboxgl={mapboxgl}\n value={inputValue}\n proximity={center}\n onChange={(d) => setInputValue(d)}\n marker\n />\n </div>\n <div ref={mapContainerRef} style={{ height: '100vh' }} />\n </>\n );\n}\n```\n\n## Search JS Integration Summary\n\n**Install:**\n\n```bash\nnpm install @mapbox/search-js-react # React\nnpm install @mapbox/search-js-web # Vanilla/Vue/Svelte\n```\n\nBoth packages include `@mapbox/search-js-core` as a dependency. Only install `-core` directly if building a custom search UI.\n\n**Key configuration options:**\n\n- `accessToken`: Your Mapbox public token\n- `map`: Map instance (must be initialized first)\n- `mapboxgl`: The mapboxgl library reference\n- `proximity`: `[lng, lat]` to bias results geographically\n- `marker`: Boolean to show/hide result marker\n- `placeholder`: Search box placeholder text\n\n### Positioning Search Box\n\n**Absolute positioning (overlay):**\n\n```jsx\n<div\n style={{\n position: 'absolute',\n top: 10,\n right: 10,\n zIndex: 10,\n width: 300\n }}\n>\n <SearchBox {...props} />\n</div>\n```\n\n**Common positions:**\n\n- Top-right: `top: 10px, right: 10px`\n- Top-left: `top: 10px, left: 10px`\n- Bottom-left: `bottom: 10px, left: 10px`\n\n## Common Mistakes (Critical)\n\n### Mistake 1: Forgetting to call map.remove()\n\n```javascript\n// BAD - Memory leak!\nuseEffect(() => {\n const map = new mapboxgl.Map({ ... })\n // No cleanup function\n}, [])\n\n// GOOD - Proper cleanup\nuseEffect(() => {\n const map = new mapboxgl.Map({ ... })\n return () => map.remove() // Cleanup\n}, [])\n```\n\n**Why:** Every Map instance creates WebGL contexts, event listeners, and DOM nodes. Without cleanup, these accumulate and cause memory leaks.\n\n### Mistake 2: Initializing map in render\n\n```javascript\n// BAD - Infinite loop in React!\nfunction MapComponent() {\n const map = new mapboxgl.Map({ ... }) // Runs on every render\n return <div />\n}\n\n// GOOD - Initialize in effect\nfunction MapComponent() {\n useEffect(() => {\n const map = new mapboxgl.Map({ ... })\n }, [])\n return <div />\n}\n```\n\n**Why:** React components re-render frequently. Creating a new map on every render causes infinite loops and crashes.\n\n### Mistake 3: Not storing map instance properly\n\n```javascript\n// BAD - map variable lost between renders\nfunction MapComponent() {\n useEffect(() => {\n let map = new mapboxgl.Map({ ... })\n // map variable is not accessible later\n }, [])\n}\n\n// GOOD - Store in useRef\nfunction MapComponent() {\n const mapRef = useRef()\n useEffect(() => {\n mapRef.current = new mapboxgl.Map({ ... })\n // mapRef.current accessible throughout component\n }, [])\n}\n```\n\n**Why:** You need to access the map instance for operations like adding layers, markers, or calling `remove()`.\n\n### Mistake 4: Storing map instance in Vue's data() (Vue-specific)\n\n```javascript\n// BAD - Vue's reactivity wraps data() objects in a Proxy, breaking mapbox-gl internals!\nexport default {\n data() {\n return {\n map: null // Will be wrapped in a Proxy\n }\n },\n mounted() {\n this.map = new mapboxgl.Map({ ... }) // Proxy breaks GL internals\n }\n}\n\n// GOOD - Assign map as a plain instance property, not in data()\nexport default {\n mounted() {\n this.map = new mapboxgl.Map({\n container: this.$refs.mapContainer,\n center: [-71.05953, 42.3629],\n zoom: 13\n })\n },\n unmounted() {\n this.map?.remove()\n }\n}\n```\n\n**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.\n\n### Mistake 5: Silent style/tile failures (no error handler)\n\n```javascript\n// BAD — blank map when the token/style fails\nconst map = new mapboxgl.Map({ ... });\n\n// GOOD — surface failures\nmap.on('error', (e) => {\n console.error(e.error || e);\n // optionally show an on-page error message\n});\n```\n\n### Mistake 6: Broken deck.gl CDN via jsDelivr `+esm`\n\n```html\n<!-- BAD — often throws: does not provide export named 'makeBatchFromTable' -->\n<script type=\"module\">\n import { MapboxOverlay } from 'https://cdn.jsdelivr.net/npm/@deck.gl/mapbox@9.0.0/+esm';\n</script>\n\n<!-- GOOD — UMD bundle (or esm.sh) -->\n<script src=\"https://unpkg.com/deck.gl@9.1.14/dist.min.js\"></script>\n<script>\n const { MapboxOverlay, ScatterplotLayer } = deck;\n map.addControl(\n new MapboxOverlay({\n interleaved: false,\n layers: [\n /* ... */\n ]\n })\n );\n</script>\n```\n\nUse `MapboxOverlay` (Mapbox IControl), not a bare `Deck` as a map control.\n\n### Mistake 7: Draw toolbar without `draw.create`\n\nIf 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.\n\n### Mistake 8: Layers lost after `setStyle` (no `style.load` rebind)\n\n`map.setStyle(...)` replaces the style tree. Custom sources/layers/handlers added earlier are wiped unless you re-attach them.\n\n```javascript\nfunction onStyleReady() {\n // re-add sources, layers, and interaction handlers here\n}\n\nmap.on('style.load', onStyleReady);\n\ndocument.querySelectorAll('[data-style]').forEach((btn) => {\n btn.addEventListener('click', () => {\n map.setStyle(btn.dataset.style);\n // do NOT only add layers on the first 'load' — wait for style.load after every switch\n });\n});\n```\n\n**Agent anti-pattern:** style switcher buttons that call `setStyle` once with no `style.load` rebind. The first style works; every switch after looks broken.\n\n## Reference Files\n\nLoad these for framework-specific patterns and additional details:\n\n- `references/vue.md` — Vue Integration (mounted/unmounted lifecycle)\n- `references/svelte.md` — Svelte Integration (onMount/onDestroy)\n- `references/angular.md` — Angular Integration with SSR handling\n- `references/vanilla.md` — Vanilla JS (Vite) + Vanilla JS (CDN)\n- `references/web-components.md` — Web Components (basic + reactive + usage in React/Vue/Svelte)\n- `references/nextjs.md` — Next.js App Router + Pages Router\n- `references/common-mistakes.md` — Common Mistakes 4-7 + Testing Patterns\n- `references/token-management.md` — Token Management per bundler + Style Configuration\n\n## When to Use This Skill\n\nInvoke this skill when:\n\n- Setting up Mapbox GL JS in a new project\n- Integrating Mapbox into a specific framework (React, Vue, Svelte, Angular, Next.js)\n- Building framework-agnostic Web Components\n- Creating reusable map components for component libraries\n- Debugging map initialization issues\n- Adding Mapbox Search functionality\n- Implementing proper cleanup and lifecycle management\n- Converting between frameworks (e.g., React to Vue)\n- Reviewing code for Mapbox integration best practices\n\n## Related Skills\n\n- **mapbox-cartography**: Map design principles and styling\n- **mapbox-token-security**: Token management and security\n- **mapbox-style-patterns**: Common map style patterns\n\n## Resources\n\n- [Mapbox GL JS Documentation](https://docs.mapbox.com/mapbox-gl-js/)\n- [Mapbox Search JS Documentation](https://docs.mapbox.com/mapbox-search-js/)\n- [create-web-app GitHub](https://github.com/mapbox/create-web-app)\n"
}SHA-256 of public snapshot: 1e1addabbb243e13ab9b01f258c05b2cffad67011872078d356abb82e23d9147