react-leaflet v5 — Componentes React para Leaflet. Mapa interativo com GeoJSON, marcadores e clusters.
bun add react-leaflet leaflet
bun add -D @types/leaflet
Importar no index.css ou main.tsx:
@import "leaflet/dist/leaflet.css";
.map-container {
width: 100%;
height: 100vh;
}
import { MapContainer, TileLayer } from 'react-leaflet'
<MapContainer
center={[-15.78, -47.93]}
zoom={4}
className="map-container"
zoomControl={false}
scrollWheelZoom={true}
>
<TileLayer
attribution='© CartoDB'
url="https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png"
/>
</MapContainer>
[!IMPORTANT] Props do
MapContainersão imutáveis após a primeira renderização. Para mudar o centro/zoom dinamicamente, useuseMap()dentro de um componente filho.
import { GeoJSON } from 'react-leaflet'
<GeoJSON
key={JSON.stringify(data)} // Forçar re-render quando dados mudam
data={geojsonData}
style={(feature) => ({
fillColor: getColor(feature.properties.value),
weight: 1,
opacity: 1,
color: '#666',
fillOpacity: 0.7,
})}
onEachFeature={(feature, layer) => {
layer.on({
click: () => handleStateClick(feature.properties.uf),
mouseover: (e) => e.target.setStyle({ weight: 2 }),
mouseout: (e) => geojsonRef.current?.resetStyle(e.target),
})
}}
/>
[!IMPORTANT] O GeoJSON do react-leaflet não re-renderiza quando
datamuda. Usekeypara forçar re-criação.
import { Marker, Popup } from 'react-leaflet'
import L from 'leaflet'
const branchIcon = new L.Icon({
iconUrl: '/branch-marker.svg',
iconSize: [24, 24],
iconAnchor: [12, 12],
})
<Marker position={[-23.55, -46.63]} icon={branchIcon}>
<Popup>
<strong>Loja Centro SP</strong><br/>
São Paulo - SP
</Popup>
</Marker>
import { CircleMarker, Tooltip } from 'react-leaflet'
<CircleMarker
center={[-23.55, -46.63]}
radius={Math.sqrt(demandValue) / 100}
pathOptions={{
fillColor: '#7c3aed',
fillOpacity: 0.18,
color: 'transparent',
}}
>
<Tooltip>Demanda: R$ 15M</Tooltip>
</CircleMarker>
Acessa a instância do Leaflet Map dentro de qualquer filho do MapContainer:
import { useMap } from 'react-leaflet'
function FlyToState({ uf, center }) {
const map = useMap()
useEffect(() => {
if (center) map.flyTo(center, 6)
}, [center])
return null
}
import { useMapEvents } from 'react-leaflet'
function MapClickHandler({ onStateClick }) {
useMapEvents({
click: (e) => {
// Quando clica no mapa (fora de qualquer feature)
},
})
return null
}
bun add react-leaflet-cluster
import MarkerClusterGroup from 'react-leaflet-cluster'
<MarkerClusterGroup chunkedLoading>
{branches.map(b => (
<Marker key={b.id} position={[b.lat, b.lng]}>
<Popup>{b.name}</Popup>
</Marker>
))}
</MarkerClusterGroup>
key que muda quando os dados mudamfillOpacity: 0.18 e color: transparent para não bloquear cliques