
Mapbox Store Locator Patterns
Ship a Mapbox-powered store or location finder with the right marker strategy, GeoJSON schema, and optional directions.
Overview
mapbox-store-locator-patterns is an agent skill for the Build phase that teaches Mapbox store-locator architecture, GeoJSON shape, and marker strategies from HTML pins to clustering.
Install
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-store-locator-patternsWhat is this skill?
- Six-part architecture: map, GeoJSON locations, list, search/filter, user location + distance, optional directions
- GeoJSON FeatureCollection template with id, name, address, phone, category, and hours properties
- Three marker strategies by scale: under 100 HTML markers, 100–1000 symbol layers, over 1000 clustering
- Ready HTML Marker and symbol-layer code snippets with popups
- Decision table ties location count to performance-safe Mapbox implementation
- 6 core locator components
- 3 marker strategies by location count thresholds (<100, 100–1000, >1000)
Adoption & trust: 764 installs on skills.sh; 64 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need a location finder but do not know how to structure GeoJSON, link a list to the map, or choose markers versus clustering for your pin count.
Who is it for?
Indie SaaS or ecommerce sites with physical locations who already use or plan Mapbox GL JS.
Skip if: Teams that only need a static embedded Google Map iframe with no search, clustering, or custom UX.
When should I use this skill?
Building or refactoring a Mapbox store locator, location finder, or retail map with list, search, and distance.
What do I get? / Deliverables
You get a concrete component checklist, data schema, and Mapbox implementation pattern matched to your location volume.
- GeoJSON store schema and architecture checklist
- Marker strategy choice and starter Mapbox snippet
Recommended Skills
Journey fit
Store locator UX is implemented during product build when wiring maps, lists, search, and distance into the customer-facing app. Patterns center on Mapbox GL markers, symbol layers, clustering, and interactive map UI—not backend-only APIs.
How it compares
Pattern reference for Mapbox locators—not a hosted store-locator SaaS or MCP geocoding server.
Common Questions / FAQ
Who is mapbox-store-locator-patterns for?
Solo and indie builders shipping web apps with Mapbox who want a proven locator layout without re-reading Mapbox docs for every feature.
When should I use mapbox-store-locator-patterns?
During Build when you add a store map, tune performance for hundreds of pins, or align list click behavior with map popups and filters.
Is mapbox-store-locator-patterns safe to install?
It is documentation-style guidance with example JavaScript; review the Security Audits panel on this Prism page before trusting any third-party skill package.
SKILL.md
READMESKILL.md - Mapbox Store Locator Patterns
# Store Locator Patterns Quick reference for building store locators and location finders with Mapbox. ## Architecture **Core Components:** 1. Map with markers 2. Location data (GeoJSON) 3. Interactive list 4. Search/filter 5. User location + distance 6. Directions (optional) ## Data Structure ```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, DC 20001", "phone": "(202) 555-0123", "category": "retail", "hours": "Mon-Sat: 9am-9pm" } } ] } ``` ## Marker Strategies | Locations | Strategy | Implementation | | ------------ | ------------ | ------------------------------ | | **< 100** | HTML Markers | `new mapboxgl.Marker()` | | **100-1000** | Symbol Layer | `addLayer({ type: 'symbol' })` | | **> 1000** | Clustering | `cluster: true` in source | ## HTML Markers Pattern ```javascript stores.features.forEach((store) => { const marker = new mapboxgl.Marker() .setLngLat(store.geometry.coordinates) .setPopup( new mapboxgl.Popup().setHTML(` <h3>${store.properties.name}</h3> <p>${store.properties.address}</p> `) ) .addTo(map); }); ``` ## Symbol Layer Pattern ```javascript map.on('load', () => { map.addSource('stores', { type: 'geojson', data: stores }); map.addLayer({ id: 'stores', type: 'symbol', source: 'stores', layout: { 'icon-image': 'custom-marker', 'icon-size': 0.8, 'text-field': ['get', 'name'], 'text-offset': [0, 1.5] } }); // Using Interactions API (recommended) map.addInteraction('store-click', { type: 'click', target: { layerId: 'stores' }, handler: (e) => { const store = e.feature; showStoreDetails(store); } }); // Or using traditional event listener // map.on('click', 'stores', (e) => { // const store = e.features[0]; // showStoreDetails(store); // }); }); ``` ## Clustering Pattern ```javascript map.addSource('stores', { type: 'geojson', data: stores, cluster: true, clusterMaxZoom: 14, clusterRadius: 50 }); // Clusters map.addLayer({ id: 'clusters', type: 'circle', source: 'stores', filter: ['has', 'point_count'], paint: { 'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 10, '#f1f075', 30, '#f28cb1'], 'circle-radius': ['step', ['get', 'point_count'], 20, 10, 30, 30, 40] } }); // Unclustered points map.addLayer({ id: 'unclustered-point', type: 'circle', source: 'stores', filter: ['!', ['has', 'point_count']], paint: { 'circle-color': '#11b4da', 'circle-radius': 8 } }); ``` ## Interactive List ```javascript function buildLocationList(stores) { const container = document.getElementById('listings'); container.innerHTML = ''; stores.features.forEach((store) => { const listing = document.createElement('div'); listing.className = 'listing'; listing.innerHTML = ` <a href="#" class="title">${store.properties.name}</a> <p>${store.properties.address}</p> ${store.properties.distance ? `<p class="distance">${store.properties.distance} mi</p>` : ''} `; listing.querySelector('.title').addEventListener('click', (e) => { e.preventDefault(); flyToStore(store); createPopup(store); }); container.appendChild(listing); }); } function flyToStore(store) { map.flyTo({ center: store.geometry.coordinates, zoom: 15 }); } ``` ## Search/Filter **Text Search:** ```javascript function filterStores(query) { const filtered = { type: 'FeatureCollection', features: stores.features.filter((store) => { const name = store.properties.name.toLowerCase(); const address = store.properties.address.toLowerCase()