Skip to content

/web-maps-leaflet

Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events

shell
$ npx -y skills add agents-inc/skills --skill web-maps-leaflet --agent claude-code

How 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
How auto-invocation works

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.md
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:
    '&copy; <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("Hov
Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withagents-inc-skills

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?

Get the whole plugin, auto-invoked