> leaflet
Build interactive maps with Leaflet.js. Use when a user asks to create map-based applications, add markers and popups, draw shapes, handle geolocation, or integrate tile layers in web applications.
curl "https://skillshub.wtf/TerminalSkills/skills/leaflet?format=md"Leaflet — Lightweight Open-Source Maps
Overview
You are an expert in Leaflet, the lightweight open-source JavaScript library for interactive maps. You help developers build map-based applications using OpenStreetMap tiles (free, no API key), custom markers, GeoJSON layers, clustering, and React integration via react-leaflet.
Instructions
React Integration
import { MapContainer, TileLayer, Marker, Popup, GeoJSON, useMap } from "react-leaflet";
import MarkerClusterGroup from "react-leaflet-cluster";
import "leaflet/dist/leaflet.css";
function StoreMap({ stores }) {
return (
<MapContainer center={[40.75, -73.98]} zoom={12}
style={{ height: "100vh", width: "100%" }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='© OpenStreetMap contributors'
/>
{/* Cluster markers when zoomed out */}
<MarkerClusterGroup>
{stores.map((store) => (
<Marker key={store.id} position={[store.lat, store.lng]}>
<Popup>
<h3>{store.name}</h3>
<p>{store.address}</p>
<p>Hours: {store.hours}</p>
</Popup>
</Marker>
))}
</MarkerClusterGroup>
</MapContainer>
);
}
// GeoJSON boundaries (neighborhoods, delivery zones)
function DeliveryZones({ zones }) {
return (
<MapContainer center={[40.75, -73.98]} zoom={11}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<GeoJSON
data={zones}
style={(feature) => ({
fillColor: feature.properties.active ? "#22c55e" : "#ef4444",
weight: 2,
opacity: 0.8,
fillOpacity: 0.3,
})}
onEachFeature={(feature, layer) => {
layer.bindPopup(`
<b>${feature.properties.name}</b><br/>
Orders: ${feature.properties.orderCount}<br/>
Avg delivery: ${feature.properties.avgDeliveryMin} min
`);
}}
/>
</MapContainer>
);
}
Custom Icons and Controls
import L from "leaflet";
// Custom marker icon
const storeIcon = new L.Icon({
iconUrl: "/icons/store-pin.png",
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32],
});
// Custom map control (e.g., "Locate Me" button)
function LocateControl() {
const map = useMap();
return (
<button
className="leaflet-control"
onClick={() => map.locate({ setView: true, maxZoom: 16 })}
>
📍 My Location
</button>
);
}
Installation
npm install leaflet react-leaflet
npm install react-leaflet-cluster # Marker clustering
npm install @types/leaflet # TypeScript types
Examples
Example 1: User asks to set up leaflet
User: "Help me set up leaflet for my project"
The agent should:
- Check system requirements and prerequisites
- Install or configure leaflet
- Set up initial project structure
- Verify the setup works correctly
Example 2: User asks to build a feature with leaflet
User: "Create a dashboard using leaflet"
The agent should:
- Scaffold the component or configuration
- Connect to the appropriate data source
- Implement the requested feature
- Test and validate the output
Guidelines
- Free tiles — Use OpenStreetMap tiles (no API key, no cost); switch to Mapbox/Stadia for custom styling
- Marker clustering — Use
react-leaflet-clusterfor 100+ markers; prevents visual clutter and improves performance - GeoJSON for regions — Use GeoJSON layers for boundaries, zones, and polygons; style dynamically based on data
- Lazy load — Leaflet CSS and JS are ~40KB; lazy load the map component for better initial page load
- SSR compatibility — Leaflet requires
window; usedynamic(() => import("./Map"), { ssr: false })in Next.js - Tile caching — Use service workers to cache map tiles for offline support in PWAs
- Event handling — Use
useMapEventshook for click, zoom, move events; build interactive experiences - Lightweight alternative — At 42KB (gzipped), Leaflet is 5x smaller than Mapbox GL JS; choose Leaflet when you don't need 3D or custom styles
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.