Source profileQuality 92/100

agents-inc/skills/src/skills/web-maps-mapbox/SKILL.md

web-maps-mapbox

Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions

Source repository stars
23
Declared platforms
0
Static risk flags
0
Last source update
2026-08-09
Source checked
2026-08-28

Decision brief

What it does: where it fits

Quick Guide: Use Mapbox GL JS v3 for interactive vector maps. Initialize with new mapboxgl.Map(), add data via sources (GeoJSON, vector), visualize with layers (fill, line, circle, symbol, fill-extrusion, heatmap), style dynamically with expressions. Use the Standard style as th…

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/agents-inc/skills --skill "src/skills/web-maps-mapbox"
    Safe inspection promptEditorial

    Inspect the Agent Skill "web-maps-mapbox" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/web-maps-mapbox/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      CRITICAL: Before Using This Skill

      All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

      Rendering interactive vector tile maps with custom stylingDisplaying point/line/polygon data on a map with data-driven stylingBuilding map-based UIs with markers, popups, and custom controls
    2. 02

      Philosophy

      Mapbox GL JS renders vector tiles on the GPU using WebGL 2, enabling smooth 60fps map interactions with large datasets. The core mental model is sources + layers + expressions:

      Sources hold the data (GeoJSON, vector tiles, raster tiles, images)Layers define how to visualize sources (fill, line, circle, symbol, fill-extrusion, heatmap, raster)Expressions make layers data-driven (color by property, size by zoom, filter by attribute)
    3. 03

      Core Patterns

      Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.

      Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.Why good: Named constants for coordinates/zoom, waits for load before data operations, uses Standard styleSee examples/core.md Pattern 1 for cleanup patterns and bad examples.
    4. 04

      Pattern 1: Map Initialization

      Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.

      Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.Why good: Named constants for coordinates/zoom, waits for load before data operations, uses Standard styleSee examples/core.md Pattern 1 for cleanup patterns and bad examples.
    5. 05

      Pattern 2: Markers, Popups, and Controls

      Markers are DOM elements placed at coordinates. Popups display content on click. Controls add navigation UI.

      Markers are DOM elements placed at coordinates. Popups display content on click. Controls add navigation UI.Why good: Popup bound to marker (opens on click automatically), controls positioned explicitly, named color constantSee examples/core.md Pattern 2 for custom marker elements and programmatic popup examples.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score92/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars23SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    agents-inc/skills
    Skill path
    src/skills/web-maps-mapbox/SKILL.md
    Commit
    81d43a51211aca12c85dcc16085fa99014ec548e
    License
    MIT
    Collected
    2026-08-28
    Default branch
    main
    View the original SKILL.md

    Mapbox GL JS Patterns

    Quick Guide: Use Mapbox GL JS v3 for interactive vector maps. Initialize with new mapboxgl.Map(), add data via sources (GeoJSON, vector), visualize with layers (fill, line, circle, symbol, fill-extrusion, heatmap), style dynamically with expressions. Use the Standard style as the default base with slots (bottom, middle, top) for layer placement. Enable clustering on GeoJSON sources for large point datasets. Use setTerrain + setFog for 3D terrain. Types are included in the mapbox-gl package (no @types/mapbox-gl needed).


    <critical_requirements>

    CRITICAL: Before Using This Skill

    All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

    (You MUST add sources before layers that reference them -- adding a layer without its source throws a runtime error)

    (You MUST listen for load or style.load before calling addSource/addLayer -- the style is not ready on construction)

    (You MUST clean up map instances with map.remove() on unmount -- leaks GPU memory and event listeners)

    (You MUST use named constants for coordinates, zoom levels, and style values -- NO magic numbers)

    (You MUST use expressions for data-driven styling instead of iterating features and setting styles individually)

    </critical_requirements>


    Auto-detection: Mapbox, mapbox-gl, mapboxgl, Map, Marker, Popup, NavigationControl, GeolocateControl, addSource, addLayer, GeoJSON source, vector source, expressions, flyTo, easeTo, fitBounds, setTerrain, setFog, fill-extrusion, clustering, slot, Standard style, mapbox-gl-geocoder, mapbox-gl-directions, mapbox-gl-draw

    When to use:

    • Rendering interactive vector tile maps with custom styling
    • Displaying point/line/polygon data on a map with data-driven styling
    • Building map-based UIs with markers, popups, and custom controls
    • Visualizing large datasets with clustering, heatmaps, or 3D extrusions
    • Adding geocoding search, routing directions, or drawing tools
    • Creating 3D terrain visualizations with elevation data

    When NOT to use:

    • Static map images without interactivity (use Mapbox Static Images API)
    • Simple embedded maps without custom data (a basic iframe embed suffices)
    • Applications requiring offline-only maps without a Mapbox access token

    Key patterns covered:

    • Map initialization with Standard style and access token
    • Markers, popups, and built-in controls
    • Source/layer model (GeoJSON, vector, raster-dem)
    • Expression-based data-driven styling
    • Clustering with automatic expansion on click
    • 3D terrain, fog, and fill-extrusion buildings
    • Camera animation (flyTo, easeTo, fitBounds)
    • Event handling (click, mouseenter, mouseleave on layers)
    • v3 slot system and Standard style configuration

    Detailed Resources:


    Philosophy

    Mapbox GL JS renders vector tiles on the GPU using WebGL 2, enabling smooth 60fps map interactions with large datasets. The core mental model is sources + layers + expressions:

    1. Sources hold the data (GeoJSON, vector tiles, raster tiles, images)
    2. Layers define how to visualize sources (fill, line, circle, symbol, fill-extrusion, heatmap, raster)
    3. Expressions make layers data-driven (color by property, size by zoom, filter by attribute)

    This separation means one source can power multiple layers (e.g., same GeoJSON rendered as both a fill layer and a line layer for borders), and layers can be styled entirely through expressions without touching the data.

    v3 Standard style: The default style is mapbox://styles/mapbox/standard, which includes 3D buildings, terrain-aware rendering, and a slot system (bottom, middle, top) for inserting custom layers at predetermined positions in the visual stack. Use setConfigProperty to customize the Standard style's appearance without replacing it.

    TypeScript: Types are bundled with mapbox-gl since v3 -- do not install @types/mapbox-gl.


    Core Patterns

    Pattern 1: Map Initialization

    Initialize with container, style, center, zoom. Always wait for load event before adding sources/layers.

    import mapboxgl from "mapbox-gl";
    import "mapbox-gl/dist/mapbox-gl.css";
    
    const DEFAULT_CENTER: [number, number] = [-74.006, 40.7128]; // [lng, lat]
    const DEFAULT_ZOOM = 12;
    
    mapboxgl.accessToken = process.env.MAPBOX_ACCESS_TOKEN!;
    
    const map = new mapboxgl.Map({
      container: "map", // HTML element ID or element reference
      style: "mapbox://styles/mapbox/standard",
      center: DEFAULT_CENTER,
      zoom: DEFAULT_ZOOM,
    });
    
    map.on("load", () => {
      // Safe to add sources and layers here
    });
    

    Why good: Named constants for coordinates/zoom, waits for load before data operations, uses Standard style

    See examples/core.md Pattern 1 for cleanup patterns and bad examples.


    Pattern 2: Markers, Popups, and Controls

    Markers are DOM elements placed at coordinates. Popups display content on click. Controls add navigation UI.

    const MARKER_COLOR = "#e74c3c";
    
    const popup = new mapboxgl.Popup({ offset: 25, maxWidth: "300px" }).setHTML(
      "<h3>Location</h3><p>Description</p>",
    );
    
    new mapboxgl.Marker({ color: MARKER_COLOR })
      .setLngLat([-74.006, 40.7128])
      .setPopup(popup)
      .addTo(map);
    
    map.addControl(new mapboxgl.NavigationControl(), "top-right");
    map.addControl(
      new mapboxgl.GeolocateControl({ trackUserLocation: true }),
      "top-right",
    );
    map.addControl(new mapboxgl.ScaleControl({ unit: "metric" }), "bottom-left");
    

    Why good: Popup bound to marker (opens on click automatically), controls positioned explicitly, named color constant

    See examples/core.md Pattern 2 for custom marker elements and programmatic popup examples.


    Pattern 3: Source and Layer Model

    Add a GeoJSON source, then one or more layers that reference it. Sources and layers are independent -- one source can feed multiple layers.

    map.on("load", () => {
      map.addSource("parks", {
        type: "geojson",
        data: {
          type: "FeatureCollection",
          features: [
            {
              type: "Feature",
              geometry: {
                type: "Polygon",
                coordinates: [
                  /* ... */
                ],
              },
              properties: { name: "Central Park", area: 3.41 },
            },
          ],
        },
      });
    
      map.addLayer({
        id: "parks-fill",
        type: "fill",
        source: "parks",
        slot: "middle", // v3 Standard style slot
        paint: {
          "fill-color": "#2ecc71",
          "fill-opacity": 0.5,
        },
      });
    
      map.addLayer({
        id: "parks-outline",
        type: "line",
        source: "parks",
        slot: "middle",
        paint: {
          "line-color": "#27ae60",
          "line-width": 2,
        },
      });
    });
    

    Why good: Source defined once, two layers visualize it differently, slot: "middle" places layers correctly in Standard style

    See examples/layers.md Pattern 1-2 for all source types and layer configuration.


    Pattern 4: Data-Driven Styling with Expressions

    Expressions are JSON arrays that style features based on their properties or zoom level.

    map.addLayer({
      id: "population-circles",
      type: "circle",
      source: "cities",
      paint: {
        // Size by population
        "circle-radius": [
          "interpolate",
          ["linear"],
          ["get", "population"],
          10000,
          5,
          100000,
          15,
          1000000,
          30,
        ],
        // Color by category
        "circle-color": [
          "match",
          ["get", "type"],
          "capital",
          "#e74c3c",
          "major",
          "#3498db",
          "#95a5a6", // fallback
        ],
      },
    });
    

    Why good: Expressions handle all styling on the GPU -- no JavaScript loops over features, scales with any dataset size

    See examples/layers.md Pattern 3-4 for expression operators and filter expressions.


    Pattern 5: Clustering

    Enable clustering on a GeoJSON source for large point datasets. Use three layers: cluster circles, count labels, unclustered points.

    const CLUSTER_RADIUS = 50;
    const CLUSTER_MAX_ZOOM = 14;
    
    map.addSource("earthquakes", {
      type: "geojson",
      data: "/data/earthquakes.geojson",
      cluster: true,
      clusterMaxZoom: CLUSTER_MAX_ZOOM,
      clusterRadius: CLUSTER_RADIUS,
    });
    

    Why good: Clustering is handled entirely by the source -- no external library needed, automatic point_count property on clusters

    See examples/layers.md Pattern 5 for complete cluster layers and click-to-expand interaction.


    Pattern 6: Camera Animation

    flyTo for dramatic transitions, easeTo for smooth pans, fitBounds for fitting data in view.

    const FLY_ZOOM = 15;
    const FLY_SPEED = 1.2;
    const BOUNDS_PADDING_PX = 50;
    
    map.flyTo({
      center: [-122.4194, 37.7749],
      zoom: FLY_ZOOM,
      speed: FLY_SPEED,
      essential: true, // not affected by prefers-reduced-motion
    });
    
    map.fitBounds(
      [
        [-122.5, 37.7],
        [-122.3, 37.8],
      ], // [sw, ne]
      { padding: BOUNDS_PADDING_PX },
    );
    

    Why good: essential: true ensures critical navigation animations still play even with reduced-motion preferences, padding keeps data away from edges

    See examples/core.md Pattern 4 for easeTo, moveend listener, and bearing/pitch animation.


    Pattern 7: Layer Event Handling

    Listen for events on specific layers for interactive features (click popups, hover effects).

    map.on("click", "parks-fill", (e) => {
      const feature = e.features?.[0];
      if (!feature) return;
    
      const coordinates = e.lngLat;
      const name = feature.properties?.name ?? "Unknown";
    
      new mapboxgl.Popup()
        .setLngLat(coordinates)
        .setHTML(`<strong>${name}</strong>`)
        .addTo(map);
    });
    
    // Cursor feedback on hover
    map.on("mouseenter", "parks-fill", () => {
      map.getCanvas().style.cursor = "pointer";
    });
    map.on("mouseleave", "parks-fill", () => {
      map.getCanvas().style.cursor = "";
    });
    

    Why good: Events scoped to a specific layer (not the whole map), cursor change signals interactivity

    See examples/core.md Pattern 5 for feature-state hover highlighting.


    Pattern 8: 3D Terrain and Fog

    Add elevation with a raster-dem source and atmospheric effects with fog.

    const TERRAIN_EXAGGERATION = 1.5;
    const TERRAIN_MAX_ZOOM = 14;
    const TERRAIN_TILE_SIZE = 512;
    
    map.on("style.load", () => {
      map.addSource("mapbox-dem", {
        type: "raster-dem",
        url: "mapbox://mapbox.mapbox-terrain-dem-v1",
        tileSize: TERRAIN_TILE_SIZE,
        maxzoom: TERRAIN_MAX_ZOOM,
      });
    
      map.setTerrain({ source: "mapbox-dem", exaggeration: TERRAIN_EXAGGERATION });
    
      map.setFog({
        range: [-1, 2],
        "horizon-blend": 0.3,
        color: "white",
        "high-color": "#add8e6",
        "space-color": "#d8f2ff",
        "star-intensity": 0.0,
      });
    });
    

    Why good: Named exaggeration constant, terrain source separate from visual layers, fog adds atmospheric depth

    See examples/interaction.md Pattern 1-2 for fog presets and fill-extrusion 3D buildings.


    Pattern 9: v3 Standard Style Configuration

    Customize the Standard style's built-in appearance without replacing it.

    // At initialization
    const map = new mapboxgl.Map({
      container: "map",
      style: "mapbox://styles/mapbox/standard",
      config: {
        basemap: {
          lightPreset: "dusk",
          showPointOfInterestLabels: false,
        },
      },
    });
    
    // At runtime
    map.setConfigProperty("basemap", "lightPreset", "night");
    map.setConfigProperty("basemap", "showPlaceLabels", true);
    

    Why good: Configuration API modifies the Standard style's built-in features without needing to understand its internal layer structure


    <decision_framework>

    Decision Framework

    Choosing a Layer Type

    What geometry are you displaying?
    |
    +-> Points?
    |   +-> Few (<100) with custom HTML? -> Markers (DOM-based)
    |   +-> Many or data-driven styling? -> circle layer or symbol layer
    |   +-> Heatmap visualization? -> heatmap layer
    |
    +-> Lines/routes?
    |   +-> line layer (width, color, dash patterns)
    |
    +-> Polygons?
    |   +-> Flat colored areas? -> fill layer
    |   +-> 3D extruded shapes? -> fill-extrusion layer
    |
    +-> Raster imagery?
        +-> raster layer (satellite, custom tiles)
    

    Choosing a Source Type

    Where is your data?
    |
    +-> Local/API GeoJSON? -> type: "geojson"
    |   +-> Dynamic updates? -> Use map.getSource(id).setData(newData)
    |   +-> Large point dataset? -> Enable cluster: true
    |
    +-> Mapbox tileset or third-party vector tiles? -> type: "vector"
    |
    +-> Elevation data? -> type: "raster-dem"
    |
    +-> Image overlay? -> type: "image" (with coordinates bounds)
    

    Markers vs Circle Layers

    How many points?
    |
    +-> < 100 with custom HTML/interaction? -> Markers (DOM elements)
    +-> 100-10,000? -> circle layer (GPU-rendered)
    +-> 10,000+? -> circle layer with clustering enabled on source
    

    Styling Approach

    Is the style static (same for all features)?
    |
    +-> YES -> Use literal paint values: "circle-color": "#e74c3c"
    +-> NO -> Does it depend on a data property?
        +-> Discrete categories? -> "match" expression
        +-> Continuous range? -> "interpolate" expression
        +-> Conditional logic? -> "case" expression
        +-> Zoom-dependent? -> "interpolate" with ["zoom"]
    

    </decision_framework>


    <red_flags>

    RED FLAGS

    High Priority Issues:

    • Calling addSource/addLayer before the load event -- style is not ready, throws error
    • Adding a layer that references a source that doesn't exist -- must add source first
    • Not calling map.remove() on component unmount -- leaks GPU memory, WebGL contexts, and event listeners
    • Using Popup.setHTML() with unsanitized user input -- XSS vulnerability. Use setText() or setDOMContent() for user data
    • Iterating features to set individual styles instead of using expressions -- defeats GPU rendering, O(n) JavaScript vs O(1) GPU expressions

    Medium Priority Issues:

    • Using Markers for large datasets (100+ points) -- DOM elements are expensive, use circle/symbol layers instead
    • Not scoping layer events to a specific layer -- map.on("click", handler) fires for any click, map.on("click", "layer-id", handler) targets one layer
    • Missing cursor feedback on interactive layers -- users don't know features are clickable without mouseenter/mouseleave cursor changes
    • Hardcoding coordinates, zoom levels, or style values -- use named constants
    • Using @types/mapbox-gl package -- types are included in mapbox-gl since v3

    Gotchas & Edge Cases:

    • Coordinates are [longitude, latitude] -- reversed from the common [lat, lng] order used by some libraries
    • queryRenderedFeatures only returns features currently visible in the viewport -- for all features use querySourceFeatures
    • GeoJSON source setData() replaces the entire dataset -- for partial updates use featureState via map.setFeatureState()
    • style.load fires every time the style changes (including setStyle), load fires only once -- use style.load for operations that must survive style switches
    • Expression property access returns null for missing properties -- always provide fallback values in match/case/coalesce
    • Popup setHTML does not sanitize HTML -- any user-provided content must be sanitized before passing
    • flyTo with essential: true overrides prefers-reduced-motion -- use only for critical navigation, not decorative animations
    • Clustered sources automatically add point_count and cluster_id properties -- do not create these manually
    • getClusterExpansionZoom is async (callback-based) -- handle errors and check if map still exists before calling easeTo
    • map.getSource() returns undefined if the source doesn't exist -- always guard the return value
    • Layer slot property only works with the Standard style -- classic styles use beforeId parameter in addLayer
    • fill-extrusion layers require fill-extrusion-height property -- without it extrusions are flat (0 height)

    </red_flags>


    <critical_reminders>

    CRITICAL REMINDERS

    All code must follow project conventions in CLAUDE.md

    (You MUST add sources before layers that reference them -- adding a layer without its source throws a runtime error)

    (You MUST listen for load or style.load before calling addSource/addLayer -- the style is not ready on construction)

    (You MUST clean up map instances with map.remove() on unmount -- leaks GPU memory and event listeners)

    (You MUST use named constants for coordinates, zoom levels, and style values -- NO magic numbers)

    (You MUST use expressions for data-driven styling instead of iterating features and setting styles individually)

    Failure to follow these rules will cause runtime errors, memory leaks, and XSS vulnerabilities.

    </critical_reminders>

    Frequently asked questions

    What to verify before installation and use

    What does the web-maps-mapbox source document cover?

    Quick Guide: Use Mapbox GL JS v3 for interactive vector maps. Initialize with new mapboxgl.Map(), add data via sources (GeoJSON, vector), visualize with layers (fill, line, circle, symbol, fill-extrusion, heatmap), style dynamically with expressions. Use the Standard style as th…

    How do I install web-maps-mapbox?

    The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/web-maps-mapbox". Inspect the command and pinned source before running it.