/web-maps-leaflet
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events
$ npx -y skills add agents-inc/skills --skill web-maps-leaflet --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/web-maps-leaflet
Context preview
The summary Claude sees to decide when to auto-load this skill.
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events
SKILL.md
web-maps-leaflet.SKILL.mdname: web-maps-leaflet
description: Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events
Leaflet Interactive Map Patterns
> **Quick Guide:** Use Leaflet (v1.9.4) for lightweight interactive maps. `L.map` for initialization, `L.tileLayer` for base maps, `L.marker`/`L.popup` for points of interest, `L.geoJSON` for vector data with `onEachFeature`/`pointToLayer`/`style`/`filter` callbacks. Always include tile layer attribution. Always clean up maps with `map.remove()` on teardown. Use `L.markerClusterGroup` for 100+ markers. Use `@types/leaflet` for TypeScript support.
---
<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 call `map.remove()` when tearing down a map instance -- prevents memory leaks and orphaned event listeners)**
**(You MUST include attribution on tile layers -- most tile providers require it legally)**
**(You MUST use `L.markerClusterGroup` or canvas rendering for 100+ markers -- DOM markers do not scale)**
**(You MUST use named constants for coordinates, zoom levels, and style values -- NO magic numbers)**
</critical_requirements>
---
**Auto-detection:** Leaflet, L.map, L.tileLayer, L.marker, L.popup, L.geoJSON, L.control, L.layerGroup, L.featureGroup, L.icon, L.divIcon, L.circleMarker, L.polyline, L.polygon, L.circle, markerClusterGroup, leaflet.markercluster, @types/leaflet, leaflet.css, addTo(map), bindPopup, onEachFeature, pointToLayer, flyTo, fitBounds
**When to use:**
- Rendering interactive maps with markers, popups, and overlays
- Displaying GeoJSON data (points, lines, polygons) on a map
- Building maps with layer switching (base layers, overlays)
- Creating custom map controls and interactions
- Handling large marker datasets with clustering
**When NOT to use:**
- 3D globe or terrain visualization (consider a WebGL-based mapping library)
- Real-time collaborative map editing (consider a specialized collaborative mapping tool)
- Vector tiles or client-side styling of map tiles (Leaflet renders raster tiles natively; vector tile support requires plugins)
**Key patterns covered:**
- Map initialization with tile layers and attribution
- Markers, popups, tooltips, and custom icons
- GeoJSON layers with `onEachFeature`, `pointToLayer`, `style`, `filter`
- Layer groups, feature groups, and layer control
- Custom controls via `L.Control.extend`
- Marker clustering with `L.markerClusterGroup`
- Events and interactive behavior
- TypeScript setup with `@types/leaflet`
- Performance strategies for large datasets
---
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Map setup, tile layers, markers, popups, GeoJSON, layer control, events
- [examples/advanced.md](examples/advanced.md) - Custom controls, clustering, performance, canvas rendering, TypeScript
- [reference.md](reference.md) - Decision frameworks, API quick reference, anti-patterns
---
<philosophy>
Philosophy
Leaflet is a lightweight (~42KB gzipped) open-source library for mobile-friendly interactive maps. It provides a small, well-designed API covering the essentials, with a rich plugin ecosystem for everything else.
**Core principles:**
1. **Simplicity first** -- The core API covers 95% of map use cases. Plugins extend the rest. 2. **Layer-based architecture** -- Everything on the map is a layer (tiles, markers, GeoJSON, controls). Layers are added/removed independently. 3. **Method chaining** -- Most methods return `this`, enabling fluent builder-style setup. 4. **Event-driven interaction** -- Maps, markers, and layers emit events (`click`, `moveend`, `zoomend`). Subscribe with `.on()`. 5. **Mobile-first** -- Touch interactions, pinch zoom, and retina tile support are built in.
**When to use Leaflet:**
- Standard 2D web maps with markers, popups, and overlays
- GeoJSON visualization and interaction
- Projects needing a small bundle size
- Maps with up to ~10K markers (with clustering)
**When NOT to use Leaflet:**
- Maps requiring WebGL rendering for 100K+ features (consider a GL-based library)
- 3D visualization or globe projection
- Client-side vector tile styling (requires plugins or a different library)
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Map Initialization and Tile Layers
Create a map targeting a DOM element, set the view, and add a tile layer with attribution.
import L from "leaflet";
import "leaflet/dist/leaflet.css";
const INITIAL_CENTER: L.LatLngExpression = [51.505, -0.09];
const INITIAL_ZOOM = 13;
const MAX_ZOOM = 19;
const map = L.map("map").setView(INITIAL_CENTER, INITIAL_ZOOM);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: MAX_ZOOM,
attribution:
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);**Why good:** Named constants for coordinates and zoom, attribution included (legally required by most providers), CSS import ensures controls render correctly
// Bad -- magic numbers, missing attribution, missing CSS import
const map = L.map("map").setView([51.505, -0.09], 13);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);**Why bad:** Magic numbers for coordinates and zoom, missing attribution violates tile provider terms, missing CSS import causes broken control rendering
See [examples/core.md](examples/core.md) Pattern 1 for tile provider options and `invalidateSize` usage.
---
Pattern 2: Markers, Popups, and Tooltips
Markers pin locations on the map. Bind popups (click-to-open) or tooltips (hover) for additional information.
const MARKER_POSITION: L.LatLngExpression = [51.5, -0.09];
const marker = L.marker(MARKER_POSITION).addTo(map);
marker.bindPopup("<b>Hello</b><br>I am a popup.");
marker.bindTooltip("HovRead more
name: web-maps-leaflet description: Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events
Leaflet Interactive Map Patterns
> **Quick Guide:** Use Leaflet (v1.9.4) for lightweight interactive maps. `L.map` for initialization, `L.tileLayer` for base maps, `L.marker`/`L.popup` for points of interest, `L.geoJSON` for vector data with `onEachFeature`/`pointToLayer`/`style`/`filter` callbacks. Always include tile layer attribution. Always clean up maps with `map.remove()` on teardown. Use `L.markerClusterGroup` for 100+ markers. Use `@types/leaflet` for TypeScript support.
---
<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 call `map.remove()` when tearing down a map instance -- prevents memory leaks and orphaned event listeners)**
**(You MUST include attribution on tile layers -- most tile providers require it legally)**
**(You MUST use `L.markerClusterGroup` or canvas rendering for 100+ markers -- DOM markers do not scale)**
**(You MUST use named constants for coordinates, zoom levels, and style values -- NO magic numbers)**
</critical_requirements>
---
**Auto-detection:** Leaflet, L.map, L.tileLayer, L.marker, L.popup, L.geoJSON, L.control, L.layerGroup, L.featureGroup, L.icon, L.divIcon, L.circleMarker, L.polyline, L.polygon, L.circle, markerClusterGroup, leaflet.markercluster, @types/leaflet, leaflet.css, addTo(map), bindPopup, onEachFeature, pointToLayer, flyTo, fitBounds
**When to use:**
- Rendering interactive maps with markers, popups, and overlays
- Displaying GeoJSON data (points, lines, polygons) on a map
- Building maps with layer switching (base layers, overlays)
- Creating custom map controls and interactions
- Handling large marker datasets with clustering
**When NOT to use:**
- 3D globe or terrain visualization (consider a WebGL-based mapping library)
- Real-time collaborative map editing (consider a specialized collaborative mapping tool)
- Vector tiles or client-side styling of map tiles (Leaflet renders raster tiles natively; vector tile support requires plugins)
**Key patterns covered:**
- Map initialization with tile layers and attribution
- Markers, popups, tooltips, and custom icons
- GeoJSON layers with `onEachFeature`, `pointToLayer`, `style`, `filter`
- Layer groups, feature groups, and layer control
- Custom controls via `L.Control.extend`
- Marker clustering with `L.markerClusterGroup`
- Events and interactive behavior
- TypeScript setup with `@types/leaflet`
- Performance strategies for large datasets
---
**Detailed Resources:**
- [examples/core.md](examples/core.md) - Map setup, tile layers, markers, popups, GeoJSON, layer control, events
- [examples/advanced.md](examples/advanced.md) - Custom controls, clustering, performance, canvas rendering, TypeScript
- [reference.md](reference.md) - Decision frameworks, API quick reference, anti-patterns
---
<philosophy>
Philosophy
Leaflet is a lightweight (~42KB gzipped) open-source library for mobile-friendly interactive maps. It provides a small, well-designed API covering the essentials, with a rich plugin ecosystem for everything else.
**Core principles:**
1. **Simplicity first** -- The core API covers 95% of map use cases. Plugins extend the rest. 2. **Layer-based architecture** -- Everything on the map is a layer (tiles, markers, GeoJSON, controls). Layers are added/removed independently. 3. **Method chaining** -- Most methods return `this`, enabling fluent builder-style setup. 4. **Event-driven interaction** -- Maps, markers, and layers emit events (`click`, `moveend`, `zoomend`). Subscribe with `.on()`. 5. **Mobile-first** -- Touch interactions, pinch zoom, and retina tile support are built in.
**When to use Leaflet:**
- Standard 2D web maps with markers, popups, and overlays
- GeoJSON visualization and interaction
- Projects needing a small bundle size
- Maps with up to ~10K markers (with clustering)
**When NOT to use Leaflet:**
- Maps requiring WebGL rendering for 100K+ features (consider a GL-based library)
- 3D visualization or globe projection
- Client-side vector tile styling (requires plugins or a different library)
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Map Initialization and Tile Layers
Create a map targeting a DOM element, set the view, and add a tile layer with attribution.
import L from "leaflet";
import "leaflet/dist/leaflet.css";
const INITIAL_CENTER: L.LatLngExpression = [51.505, -0.09];
const INITIAL_ZOOM = 13;
const MAX_ZOOM = 19;
const map = L.map("map").setView(INITIAL_CENTER, INITIAL_ZOOM);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: MAX_ZOOM,
attribution:
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
}).addTo(map);**Why good:** Named constants for coordinates and zoom, attribution included (legally required by most providers), CSS import ensures controls render correctly
// Bad -- magic numbers, missing attribution, missing CSS import
const map = L.map("map").setView([51.505, -0.09], 13);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);**Why bad:** Magic numbers for coordinates and zoom, missing attribution violates tile provider terms, missing CSS import causes broken control rendering
See [examples/core.md](examples/core.md) Pattern 1 for tile provider options and `invalidateSize` usage.
---
Pattern 2: Markers, Popups, and Tooltips
Markers pin locations on the map. Bind popups (click-to-open) or tooltips (hover) for additional information.
const MARKER_POSITION: L.LatLngExpression = [51.5, -0.09];
const marker = L.marker(MARKER_POSITION).addTo(map);
marker.bindPopup("<b>Hello</b><br>I am a popup.");
marker.bindTooltip("HovShowing the first part of this file.
The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?
Repo: agents-inc/skills
Other skills on agents-inc-skills.
- /ai-infrastructure-huggingface-inference
Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints
Open skill - /ai-infrastructure-litellm
LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment
Open skill - /ai-infrastructure-modal
Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns
Open skill - /ai-infrastructure-ollama
Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint
Open skill - /ai-infrastructure-replicate
Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training
Open skill - /ai-infrastructure-together-ai
Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints
Open skill

