← 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
{
"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.",
"included_files": [
{
"relative_path": "AGENTS.md",
"size_in_bytes": 8683
},
{
"relative_path": "evals/evals.json",
"size_in_bytes": 3685
},
{
"relative_path": "references/geolocation-directions.md",
"size_in_bytes": 5354
},
{
"relative_path": "references/markers.md",
"size_in_bytes": 4399
},
{
"relative_path": "references/optimization-a11y.md",
"size_in_bytes": 2936
},
{
"relative_path": "references/search-filter.md",
"size_in_bytes": 1691
},
{
"relative_path": "references/styling-layout.md",
"size_in_bytes": 4426
},
{
"relative_path": "references/variations-react.md",
"size_in_bytes": 3412
}
],
"skill_md_contents": "---\nname: mapbox-store-locator-patterns\ndescription: Common patterns for building store locators, restaurant finders, and location-based search applications with Mapbox. Covers marker display, filtering, distance calculation, and interactive lists.\n---\n\n# Store Locator Patterns Skill\n\nComprehensive 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.\n\n## When to Use This Skill\n\nUse this skill when building applications that:\n\n- Display multiple locations on a map (stores, restaurants, offices, etc.)\n- Allow users to filter or search locations\n- Calculate distances from user location\n- Provide interactive lists synced with map markers\n- Show location details in popups or side panels\n- Integrate directions to selected locations\n\n## Dependencies\n\n**Required:**\n\n- Mapbox GL JS v3.x\n- [@turf/turf](https://turfjs.org/) - For spatial calculations (distance, area, etc.)\n\n**Installation:**\n\n```bash\nnpm install mapbox-gl @turf/turf\n```\n\n## Core Architecture\n\n### Pattern Overview\n\nA typical store locator consists of:\n\n1. **Map Display** - Shows all locations as markers\n2. **Location Data** - GeoJSON with store/location information\n3. **Interactive List** - Side panel listing all locations\n4. **Filtering** - Search, category filters, distance filters\n5. **Detail View** - Popup or panel with location details\n6. **User Location** - Geolocation for distance calculation. For the blue dot location indicator, use the built-in `mapboxgl.GeolocateControl` — simpler than custom markers.\n7. **Directions** - Route to selected location (optional)\n\n### Data Structure\n\n**GeoJSON format for locations:**\n\n```json\n{\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\n \"type\": \"Feature\",\n \"geometry\": {\n \"type\": \"Point\",\n \"coordinates\": [-77.034084, 38.909671]\n },\n \"properties\": {\n \"id\": \"store-001\",\n \"name\": \"Downtown Store\",\n \"address\": \"123 Main St, Washington, DC 20001\",\n \"phone\": \"(202) 555-0123\",\n \"hours\": \"Mon-Sat: 9am-9pm, Sun: 10am-6pm\",\n \"category\": \"retail\",\n \"website\": \"https://example.com/downtown\"\n }\n }\n ]\n}\n```\n\n**Key properties:**\n\n- `id` - Unique identifier for each location\n- `name` - Display name\n- `address` - Full address for display and geocoding\n- `coordinates` - `[longitude, latitude]` format\n- `category` - For filtering (retail, restaurant, office, etc.)\n- Custom properties as needed (hours, phone, website, etc.)\n\n## Basic Store Locator Implementation\n\n### Step 1: Initialize Map and Data\n\n```javascript\nimport mapboxgl from 'mapbox-gl';\nimport 'mapbox-gl/dist/mapbox-gl.css';\n\nmapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';\n\n// Store locations data\nconst stores = {\n type: 'FeatureCollection',\n features: [\n {\n type: 'Feature',\n geometry: {\n type: 'Point',\n coordinates: [-77.034084, 38.909671]\n },\n properties: {\n id: 'store-001',\n name: 'Downtown Store',\n address: '123 Main St, Washington, DC 20001',\n phone: '(202) 555-0123',\n category: 'retail'\n }\n }\n // ... more stores\n ]\n};\n\nconst map = new mapboxgl.Map({\n container: 'map',\n style: 'mapbox://styles/mapbox/standard',\n center: [-77.034084, 38.909671],\n zoom: 11\n});\n```\n\n### Step 2: Add Markers to Map\n\n**Marker strategy by location count:**\n\n| Count | Strategy | Reason |\n| ------------------- | -------------------------- | ------------------------------------------------------------------------------ |\n| **Fewer than 100** | HTML Markers | Full DOM/CSS control; DOM node count is manageable |\n| **100–1,000** | **Symbol Layer** (default) | Renders on the **GPU via WebGL** — one `<canvas>`, zero per-point DOM elements |\n| **More than 1,000** | Clustering | Reduces visual clutter at large scale |\n\n> 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.\n\n**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`.\n\n```javascript\nmap.on('load', () => {\n // Add store data as source\n map.addSource('stores', {\n type: 'geojson',\n data: stores\n });\n\n // Add custom marker image\n map.loadImage('/marker-icon.png', (error, image) => {\n if (error) throw error;\n map.addImage('custom-marker', image);\n\n // Add symbol layer\n map.addLayer({\n id: 'stores-layer',\n type: 'symbol',\n source: 'stores',\n layout: {\n 'icon-image': 'custom-marker',\n 'icon-size': 0.8,\n 'icon-allow-overlap': true,\n 'text-field': ['get', 'name'],\n 'text-font': ['Open Sans Bold', 'Arial Unicode MS Bold'],\n 'text-offset': [0, 1.5],\n 'text-anchor': 'top',\n 'text-size': 12\n }\n });\n });\n\n // Handle marker clicks using Interactions API (recommended)\n map.addInteraction('store-click', {\n type: 'click',\n target: { layerId: 'stores-layer' },\n handler: (e) => {\n const store = e.feature;\n flyToStore(store);\n createPopup(store);\n }\n });\n\n // Or using traditional event listener:\n // map.on('click', 'stores-layer', (e) => {\n // const store = e.features[0];\n // flyToStore(store);\n // createPopup(store);\n // });\n //\n // For why addInteraction is preferred over map.on(), and how to pair it with\n // setFeatureState/appearances for hover and selection styling, see the\n // \"interactions\" reference file in the mapbox-style-patterns skill.\n\n // Change cursor on hover\n map.on('mouseenter', 'stores-layer', () => {\n map.getCanvas().style.cursor = 'pointer';\n });\n\n map.on('mouseleave', 'stores-layer', () => {\n map.getCanvas().style.cursor = '';\n });\n});\n```\n\n### Step 3: Build Interactive Location List\n\n```javascript\nfunction buildLocationList(stores) {\n const listingContainer = document.getElementById('listings');\n\n stores.features.forEach((store, index) => {\n const listing = listingContainer.appendChild(document.createElement('div'));\n listing.id = `listing-${store.properties.id}`;\n listing.className = 'listing';\n\n const link = listing.appendChild(document.createElement('a'));\n link.href = '#';\n link.className = 'title';\n link.id = `link-${store.properties.id}`;\n link.innerHTML = store.properties.name;\n\n const details = listing.appendChild(document.createElement('div'));\n details.innerHTML = `\n <p>${store.properties.address}</p>\n <p>${store.properties.phone || ''}</p>\n `;\n\n // Handle listing click\n link.addEventListener('click', (e) => {\n e.preventDefault();\n flyToStore(store);\n createPopup(store);\n highlightListing(store.properties.id);\n });\n });\n}\n\nfunction flyToStore(store) {\n map.flyTo({\n center: store.geometry.coordinates,\n zoom: 15,\n duration: 1000\n });\n}\n\nfunction createPopup(store) {\n const popups = document.getElementsByClassName('mapboxgl-popup');\n // Remove existing popups\n if (popups[0]) popups[0].remove();\n\n new mapboxgl.Popup({ closeOnClick: true })\n .setLngLat(store.geometry.coordinates)\n .setHTML(\n `<h3>${store.properties.name}</h3>\n <p>${store.properties.address}</p>\n <p>${store.properties.phone}</p>\n ${store.properties.website ? `<a href=\"${store.properties.website}\" target=\"_blank\">Visit Website</a>` : ''}`\n )\n .addTo(map);\n}\n\n// IMPORTANT: highlightListing MUST include scrollIntoView — without it,\n// selecting a marker on the map won't scroll the sidebar to the listing.\nfunction highlightListing(id) {\n // Remove existing highlights\n const activeItem = document.getElementsByClassName('active');\n if (activeItem[0]) {\n activeItem[0].classList.remove('active');\n }\n\n // Add highlight to selected listing\n const listing = document.getElementById(`listing-${id}`);\n listing.classList.add('active');\n\n // Scroll the selected listing into view (critical UX requirement)\n listing.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n}\n\n// Build the list on load\nmap.on('load', () => {\n buildLocationList(stores);\n});\n```\n\n## Reference Files\n\nLoad these references for additional patterns as needed:\n\n| Reference | File | Contents |\n| ------------------------- | -------------------------------------- | ---------------------------------------------------------------- |\n| HTML Markers & Clustering | `references/markers.md` | HTML Markers (< 100 locations), Clustering (> 1000 locations) |\n| Search & Filter | `references/search-filter.md` | Text search, category filter |\n| Geolocation & Directions | `references/geolocation-directions.md` | User location, distance calculation, route directions |\n| Styling & Layout | `references/styling-layout.md` | Full HTML/CSS layout, custom marker CSS |\n| Performance & A11y | `references/optimization-a11y.md` | Debounced search, data management, error handling, accessibility |\n| Variations & React | `references/variations-react.md` | Mobile-first, fullscreen, map-only, React implementation |\n\n## Resources\n\n- [Turf.js](https://turfjs.org/) - Spatial analysis library (recommended for distance calculations)\n- [Mapbox GL JS API](https://docs.mapbox.com/mapbox-gl-js/)\n- [Interactions API Guide](https://docs.mapbox.com/mapbox-gl-js/guides/user-interactions/interactions/)\n- [GeoJSON Specification](https://geojson.org/)\n- [Directions API](https://docs.mapbox.com/api/navigation/directions/)\n- [Store Locator Tutorial](https://docs.mapbox.com/help/tutorials/building-a-store-locator/)\n"
}SHA-256: 932f35424c8c4fcdb833098b6e775801bb525b7cf44b028a9b637de7fe8512f6