All skills
mapbox avatar

/mapbox-web-integration-patterns

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

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.

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

This session only. Nothing lands on disk.

referencesweb-components.md

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

Web Components (Framework-Agnostic)

Web Components are a W3C standard for creating reusable custom elements that work in any framework or no framework at all.

When to use Web Components:

  • ✅ Vanilla JavaScript apps - No framework? Web Components are a great choice
  • ✅ Design systems - Building component libraries used across multiple frameworks
  • ✅ Micro-frontends - Application uses different frameworks in different parts
  • ✅ Multi-framework organizations - Teams working with React, Vue, Svelte, etc. need shared components
  • ✅ Framework migration - Transitioning from one framework to another incrementally
  • ✅ Long-term stability - W3C standard, no framework lock-in

When to use framework-specific patterns instead:

  • 🔧 Already using a framework - If you're building in React, use React patterns (simpler, better integration)
  • 🔧 Need framework features - Deep integration with React hooks, Vue Composition API, state management, routing
  • 🔧 Team familiarity - Team is proficient with framework patterns

💡 Tip: If you're using React, Vue, Svelte, or Angular, start with the framework-specific patterns. They're simpler and better integrated. Use Web Components when you need cross-framework compatibility or are building vanilla JavaScript apps.

Real-world example: A company with React (main app), Vue (admin panel), and Svelte (marketing site) can build one <mapbox-map> component that works everywhere.

Basic Web Component

import mapboxgl from 'mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

class MapboxMap extends HTMLElement {
  constructor() {
    super();
    this.map = null;
  }

  connectedCallback() {
    // Get configuration from attributes
    const token = this.getAttribute('access-token') || import.meta.env.VITE_MAPBOX_ACCESS_TOKEN;
    const mapStyle = this.getAttribute('map-style') || 'mapbox://styles/mapbox/standard';
    const center = this.getAttribute('center')?.split(',').map(Number) || [-71.05953, 42.3629];
    const zoom = parseFloat(this.getAttribute('zoom')) || 13;

    // Initialize map
    mapboxgl.accessToken = token;

    this.map = new mapboxgl.Map({
      container: this,
      style: mapStyle,
      center: center,
      zoom: zoom
    });

    // Dispatch custom event when map loads
    this.map.on('load', () => {
      this.dispatchEvent(
        new CustomEvent('mapload', {
          detail: { map: this.map }
        })
      );
    });
  }

  // CRITICAL: Clean up when element is removed
  disconnectedCallback() {
    if (this.map) {
      this.map.remove();
      this.map = null;
    }
  }

  // Expose map instance to JavaScript
  getMap() {
    return this.map;
  }
}

// Register the custom element
customElements.define('mapbox-map', MapboxMap);

Usage in HTML:

<!-- Basic usage -->
<mapbox-map
  access-token="pk.YOUR_TOKEN"
  map-style="mapbox://styles/mapbox/dark-v11"
  center="-122.4194,37.7749"
  zoom="12"
></mapbox-map>

<style>
  mapbox-map {
    display: block;
    height: 100vh;
    width: 100%;
  }
</style>

Usage in React:

import './mapbox-map-component';
function App() {
  const mapRef = useRef(null);

  useEffect(() => {
    const handleMapLoad = (e) => {
      const map = e.detail.map;
      // Add markers, layers, etc.
      new mapboxgl.Marker().setLngLat([-122.4194, 37.7749]).addTo(map);
    };

    mapRef.current?.addEventListener('mapload', handleMapLoad);

    return () => {
      mapRef.current?.removeEventListener('mapload', handleMapLoad);
    };
  }, []);

  return (
    <mapbox-map
      ref={mapRef}
      access-token={import.meta.env.VITE_MAPBOX_ACCESS_TOKEN}
      map-style="mapbox://styles/mapbox/standard"
      center="-122.4194,37.7749"
      zoom="12"
    />
  );
}

Usage in Vue:

Import the component file, then use directly in template. Vue supports custom events natively via @mapload:

<template>
  <mapbox-map
    ref="map"
    :access-token="token"
    map-style="mapbox://styles/mapbox/streets-v12"
    center="-71.05953,42.3629"
    zoom="13"
    @mapload="handleMapLoad"
  />
</template>
<script>
import './mapbox-map-component';
export default {
  data: () => ({ token: import.meta.env.VITE_MAPBOX_ACCESS_TOKEN }),
  methods: {
    handleMapLoad(e) {
      const map = e.detail.map; /* interact */
    }
  }
};
</script>

Usage in Svelte:

Use bind:this for element ref and on:mapload for custom events:

<script>
  import './mapbox-map-component';
  let mapElement;
  function handleMapLoad(e) { const map = e.detail.map; /* interact */ }
</script>
<mapbox-map bind:this={mapElement} access-token={import.meta.env.VITE_MAPBOX_ACCESS_TOKEN}
  map-style="mapbox://styles/mapbox/standard" center="-71.05953,42.3629" zoom="13"
  on:mapload={handleMapLoad} />

Advanced: Reactive Attributes Pattern

class MapboxMapReactive extends HTMLElement {
  static get observedAttributes() {
    return ['center', 'zoom', 'map-style'];
  }

  constructor() {
    super();
    this.map = null;
  }

  connectedCallback() {
    mapboxgl.accessToken = this.getAttribute('access-token');

    this.map = new mapboxgl.Map({
      container: this,
      style: this.getAttribute('map-style') || 'mapbox://styles/mapbox/standard',
      center: this.getAttribute('center')?.split(',').map(Number) || [0, 0],
      zoom: parseFloat(this.getAttribute('zoom')) || 9
    });
  }

  disconnectedCallback() {
    if (this.map) {
      this.map.remove();
      this.map = null;
    }
  }

  // React to attribute changes
  attributeChangedCallback(name, oldValue, newValue) {
    if (!this.map || oldValue === newValue) return;

    switch (name) {
      case 'center':
        const center = newValue.split(',').map(Number);
        this.map.setCenter(center);
        break;
      case 'zoom':
        this.map.setZoom(parseFloat(newValue));
        break;
      case 'map-style':
        this.map.setStyle(newValue);
        break;
    }
  }
}

customElements.define('mapbox-map-reactive', MapboxMapReactive);

Key points: Use connectedCallback() for init, always implement disconnectedCallback() with map.remove(), read config from HTML attributes, dispatch custom events (mapload), use observedAttributes + attributeChangedCallback for reactive updates. Works in any framework.

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill contains safe, standard documentation, integration patterns, and best practices for Mapbox GL JS across various web frameworks.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    1/2 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

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

Last checked against GitHub 9 hours ago.

Activeupdated 2 months ago

README badge

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

Provides official integration patterns for Mapbox GL JS across React, Vue, Svelte, Angular, and vanilla JavaScript, with lifecycle management, token handling, and Search JS integration. Covers setup in each framework, cleanup to prevent memory leaks, and common mistakes like storing map instances in Vue's data() or forgetting map.remove() calls.

Generated from the current SKILL.md.

Does this skill cover all major web frameworks?
Yes. It covers React, Vue, Svelte, Angular, Next.js, vanilla JavaScript, and Web Components. Framework-specific patterns are documented in reference files (e.g., references/vue.md, references/angular.md).
What version of Mapbox GL JS should I use?
v3.x is recommended (minimum v3.0.0). v2.x is legacy and no longer actively developed. v3.x requires WebGL 2 and has improved TypeScript types, but core initialization patterns work the same in both versions.
How do I handle the Mapbox access token securely?
Store it in environment variables (e.g., VITE_MAPBOX_ACCESS_TOKEN for Vite, REACT_APP_MAPBOX_TOKEN for Create React App). Set it via mapboxgl.accessToken or pass it per-map in the constructor. The skill has a dedicated reference file on token management per bundler.
What's the most common mistake when integrating Mapbox?
Forgetting to call map.remove() in cleanup functions. Without it, WebGL contexts and event listeners accumulate, causing memory leaks. The skill documents this and three other critical mistakes with examples.
Can I use Mapbox Search JS with these patterns?
Yes. The skill includes React + Search JS integration examples and covers SearchBox configuration, positioning, and the required packages (@mapbox/search-js-react for React, @mapbox/search-js-web for other frameworks).

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