tessl install tessl/npm-react-leaflet@5.0.3React components for Leaflet maps
Complete walkthrough for getting started with React Leaflet.
npm install react-leaflet leafletFor TypeScript projects:
npm install -D @types/leafletIn your main application file or component:
import "leaflet/dist/leaflet.css";Important: This CSS import is required for the map to display correctly.
Create a new component file Map.tsx (or Map.jsx):
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
export function Map() {
return (
<MapContainer
center={[51.505, -0.09]}
zoom={13}
style={{ height: "400px", width: "100%" }}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
);
}import { Map } from "./Map";
function App() {
return (
<div className="App">
<h1>My React Leaflet Map</h1>
<Map />
</div>
);
}
export default App;Due to how bundlers handle assets, marker icons may not display correctly. Add this configuration:
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import icon from "leaflet/dist/images/marker-icon.png";
import iconShadow from "leaflet/dist/images/marker-shadow.png";
import iconRetina from "leaflet/dist/images/marker-icon-2x.png";
let DefaultIcon = L.icon({
iconUrl: icon,
iconRetinaUrl: iconRetina,
shadowUrl: iconShadow,
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
tooltipAnchor: [16, -28],
shadowSize: [41, 41]
});
L.Marker.prototype.options.icon = DefaultIcon;Place this in a file that runs once at application startup (e.g., src/leafletSetup.ts), then import it in your main file:
import "./leafletSetup";Leaflet doesn't work with server-side rendering. Use dynamic imports:
import dynamic from 'next/dynamic';
const Map = dynamic(() => import('./Map'), {
ssr: false,
loading: () => <p>Loading map...</p>
});
export default function Page() {
return <Map />;
}import { useMapEvents } from "react-leaflet";
import { useState } from "react";
function LocationMarker() {
const [position, setPosition] = useState(null);
useMapEvents({
click(e) {
setPosition(e.latlng);
},
});
return position === null ? null : (
<Marker position={position}>
<Popup>You clicked here</Popup>
</Marker>
);
}
// Use in your map:
<MapContainer center={[51.505, -0.09]} zoom={13} style={{ height: "400px" }}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<LocationMarker />
</MapContainer>import { useMap } from "react-leaflet";
import { useEffect } from "react";
function ChangeView({ center, zoom }) {
const map = useMap();
useEffect(() => {
map.setView(center, zoom);
}, [map, center, zoom]);
return null;
}
// Usage:
function App() {
const [center, setCenter] = useState([51.505, -0.09]);
const [zoom, setZoom] = useState(13);
return (
<MapContainer center={center} zoom={zoom} style={{ height: "400px" }}>
<ChangeView center={center} zoom={zoom} />
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
</MapContainer>
);
}const locations = [
{ id: 1, position: [51.505, -0.09], title: "Location 1" },
{ id: 2, position: [51.51, -0.1], title: "Location 2" },
{ id: 3, position: [51.49, -0.08], title: "Location 3" },
];
<MapContainer center={[51.505, -0.09]} zoom={13} style={{ height: "400px" }}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
{locations.map((loc) => (
<Marker key={loc.id} position={loc.position}>
<Popup>{loc.title}</Popup>
</Marker>
))}
</MapContainer>import { Circle, Polygon } from "react-leaflet";
<MapContainer center={[51.505, -0.09]} zoom={13} style={{ height: "400px" }}>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
{/* Circle with 200m radius */}
<Circle
center={[51.505, -0.09]}
radius={200}
pathOptions={{ color: 'red', fillColor: 'blue', fillOpacity: 0.5 }}
/>
{/* Polygon */}
<Polygon
positions={[
[51.515, -0.09],
[51.52, -0.1],
[51.52, -0.12],
]}
pathOptions={{ color: 'purple' }}
/>
</MapContainer><MapContainer
center={[51.505, -0.09]}
zoom={13}
style={{ height: "100vh", width: "100%" }}
scrollWheelZoom={false}
zoomControl={false}
>
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<ZoomControl position="bottomright" />
</MapContainer>// Satellite imagery
<TileLayer
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
attribution='Tiles © Esri'
/>
// Dark theme
<TileLayer
url="https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
attribution='© OpenStreetMap © CARTO'
/>
// Terrain
<TileLayer
url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
attribution='Map data: © OpenStreetMap, SRTM | Map style: © OpenTopoMap'
maxZoom={17}
/>import L from "leaflet";
const customIcon = new L.Icon({
iconUrl: "/path/to/icon.png",
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32],
});
<Marker position={[51.505, -0.09]} icon={customIcon}>
<Popup>Custom icon marker</Popup>
</Marker>Problem: Map container is empty or shows gray area.
Solutions:
import "leaflet/dist/leaflet.css"style={{ height: "400px" }}Problem: Markers don't show on the map.
Solutions:
Problem: Type errors with Leaflet components.
Solutions:
npm install -D @types/leafletimport type { Map, LatLng } from "leaflet"Problem: "window is not defined" or similar SSR errors.
Solutions:
ssr: false (see Step 6)Now that you have a basic map working:
Here's a full working example with all the essentials:
import { MapContainer, TileLayer, Marker, Popup, Circle, useMapEvents } from "react-leaflet";
import { useState } from "react";
import "leaflet/dist/leaflet.css";
import "./leafletSetup"; // Icon configuration
function LocationMarker() {
const [position, setPosition] = useState(null);
useMapEvents({
click(e) {
setPosition(e.latlng);
},
});
return position === null ? null : (
<Marker position={position}>
<Popup>You clicked here</Popup>
</Marker>
);
}
export function InteractiveMap() {
const locations = [
{ id: 1, position: [51.505, -0.09], title: "London" },
{ id: 2, position: [51.51, -0.1], title: "Westminster" },
];
return (
<MapContainer
center={[51.505, -0.09]}
zoom={13}
style={{ height: "600px", width: "100%" }}
scrollWheelZoom={false}
>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{locations.map((loc) => (
<Marker key={loc.id} position={loc.position}>
<Popup>{loc.title}</Popup>
</Marker>
))}
<Circle
center={[51.505, -0.09]}
radius={500}
pathOptions={{ color: 'blue', fillColor: 'lightblue', fillOpacity: 0.3 }}
/>
<LocationMarker />
</MapContainer>
);
}