Skip to content
Development
Skill

/web-maps-leaflet

Leaflet interactive maps. Use when building a 2D map with tile layers, markers, popups, GeoJSON, layer control, custom controls or marker clustering.

From plugin
skills
26200 skills

Claude Code

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

SKILL.md

web-maps-leaflet.SKILL.md
name: web-maps-leaflet
description: Leaflet interactive maps. Use when building a 2D map with tile layers, markers, popups, GeoJSON, layer control, custom controls or marker clustering.

Leaflet Interactive Map Patterns

> **Quick Guide:** Leaflet is a small 2D mapping library: `L.map` initializes against a DOM element, > `L.tileLayer` supplies the base map, and everything drawn on top — markers, popups, GeoJSON, > controls — is a layer added to and removed from the map independently. `L.geoJSON` does most of > the data work through its `pointToLayer`, `onEachFeature`, `style` and `filter` callbacks. Marker > count is the decision that shapes the rest: past a hundred, DOM markers stop scaling and the work > moves to clustering or the canvas renderer. **Current: v1.9.4**, with types in `@types/leaflet`.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — map setup, tile providers, markers and icons, GeoJSON, layer groups and control, events
  • [examples/advanced.md](examples/advanced.md) — custom controls, clustering, TypeScript, canvas rendering, bounds and viewport
  • [reference.md](reference.md) — `L.map` methods, `L.geoJSON` and cluster options, event table, install checklist

---

<critical_requirements>

Before writing Leaflet code

**Call `map.remove()` when the map goes away.** It is the one call that tears down the resize observers, animation frames and DOM listeners Leaflet attached; without it a re-mount on the same element throws "Map container is already initialized".

**Give every tile layer an `attribution`.** OpenStreetMap and most other providers require it in their terms, and there is no default.

**Move past DOM markers at around a hundred points** — `L.markerClusterGroup`, or `L.circleMarker` on the canvas renderer. Each `L.marker` is an element in the document, and the page slows in proportion.

</critical_requirements>

---

**Auto-detection:** Leaflet, `L.map`, `L.tileLayer`, `L.marker`, `L.popup`, `L.geoJSON`, `L.control.layers`, `L.layerGroup`, `L.featureGroup`, `L.icon`, `L.divIcon`, `L.circleMarker`, `L.polyline`, `L.polygon`, `L.Control.extend`, `L.DomUtil`, `L.DomEvent`, markerClusterGroup, leaflet.markercluster, `@types/leaflet`, leaflet.css, addTo(map), bindPopup, bindTooltip, onEachFeature, pointToLayer, invalidateSize, flyTo, fitBounds, latLngBounds

**Applies to:**

  • Interactive 2D maps with markers, popups, tooltips and overlays
  • GeoJSON points, lines and polygons, styled and filtered from their own properties
  • Base-layer switching and overlay toggling through a layer control
  • Custom controls built on `L.Control.extend`
  • Large marker datasets, through clustering or the canvas renderer
  • Map, marker and layer events, and camera movement

**Handled elsewhere:**

  • Where the tiles come from — Leaflet renders any XYZ raster endpoint, and choosing a provider and

meeting its terms is a separate decision

  • Where the GeoJSON comes from — the layer takes an object, and fetching, caching and paging it are

not the map's concern

  • How markers, popups and controls look — the map hands you class names and containers, and the CSS

inside them is settled by whatever owns styling

  • 3D terrain, globe projection and GPU-rendered vector tiles — this is a 2D raster library with an

SVG or canvas vector layer over it

---

<philosophy>

**Everything on the map is a layer.** Tiles, markers, GeoJSON, even controls — each is added and removed independently, which is why toggling a dataset is `map.removeLayer(group)` rather than a rebuild.

**The core is deliberately small** (~42KB gzipped) and covers the common map. Clustering, heatmaps, drawing and vector tiles are plugins, and a plugin is how the library expects those needs to be met.

**Methods return `this`**, so setup reads as a chain: `L.marker(pos).addTo(map).bindPopup(html)`.

**Interaction is events.** Maps, markers and layers all emit; `.on()` subscribes and `.off()` unsubscribes, and `map.off()` with no arguments is part of teardown.

</philosophy>

---

<decision_framework>

Marker strategy, by count

< 100     → L.marker with L.icon or L.divIcon
100 – 10K → L.markerClusterGroup
10K – 50K → L.markerClusterGroup with chunkedLoading, and L.circleMarker rather than L.marker
50K+      → canvas rendering, or pre-tiled vector data

Which layer type

One coordinate               → L.marker (with an icon) or L.circleMarker (for data viz)
A path                       → L.polyline
An area                      → L.polygon, or L.circle for a radius in metres
A GeoJSON dataset            → L.geoJSON, which handles every geometry type
A group you need to toggle   → L.layerGroup, or L.featureGroup where you need getBounds()/bindPopup()

Which icon

The default pin          → L.marker() with no icon option
A custom image           → L.icon({ iconUrl, iconSize, iconAnchor })
Several image variants   → L.Icon.extend({ options }), then construct per variant
HTML or CSS content      → L.divIcon({ html, className, iconSize })
A data point, many of    → L.circleMarker — a vector shape, not a DOM element

</decision_framework>

---

<patterns>

Core patterns

Pattern 1: Map initialization and tile layers

Target a DOM element, set the view, add a base layer with its attribution.

import L from "leaflet";
import "leaflet/dist/leaflet.css";

const map = L.map("map").setView([51.505, -0.09], 13);

L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
  maxZoom: 19,
  attribution:
    '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
}).addTo(map);

The CSS import is not optional — without it controls, popups and markers render unpositioned.

Full code: [examples/core.md](examples/core.md)

---

Pattern 2: Markers, popups and tooltips

const marker = L.marker([51.5, -0.09]).addTo(map);
marker.bindPopup("<b>Hello</b><br>I am a popup.");
marker.bindTooltip("Hover text", { direction: "top" });
Read more
@agents-inc
Ships withskills

The official skills marketplace for Agents Inc. 230+ 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?

Stars on GitHub

Momentum

stars in total
26
Star history of agents-inc/skills on GitHub, a chart by star-history.com

Other skills on skills.

ai-infrastructure-litellm

ai-infrastructure-lite…

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking,…

@agents-inc@agents-incInstall Skill
ai-infrastructure-modal

ai-infrastructure-moda…

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client…

@agents-inc@agents-incInstall Skill
ai-infrastructure-ollama

ai-infrastructure-olla…

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output,…

@agents-inc@agents-incInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about web-maps-leaflet.

Which coding agents can use web-maps-leaflet?
web-maps-leaflet installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.