import {
Map,
MapLayerGroup,
MapLayers,
MapLayersControl,
MapMarker,
MapPopup,
MapTileLayer,
} from "@/components/ui/map"
import type { LatLngExpression } from "leaflet"
import { DropletIcon, MountainIcon, PawPrintIcon } from "lucide-react"
export function MapWithLayersControl() {
const PLACES = [
{
name: "Water",
places: [
{
name: "Victoria Falls",
coordinates: [-17.9243, 25.856] satisfies LatLngExpression,
icon: <DropletIcon />,
},
{
name: "Lake Malawi",
coordinates: [-12.1333, 34.5667] satisfies LatLngExpression,
icon: <DropletIcon />,
},
{
name: "Orange River",
coordinates: [-28.6327, 16.4522] satisfies LatLngExpression,
icon: <DropletIcon />,
},
],
},
{
name: "Mountains",
places: [
{
name: "Table Mountain",
coordinates: [-33.9628, 18.4098] satisfies LatLngExpression,
icon: <MountainIcon />,
},
{
name: "Mount Kenya",
coordinates: [-0.1521, 37.3084] satisfies LatLngExpression,
icon: <MountainIcon />,
},
{
name: "Kilimanjaro",
coordinates: [-3.0674, 37.3556] satisfies LatLngExpression,
icon: <MountainIcon />,
},
],
},
{
name: "Safari",
places: [
{
name: "Etosha National Park",
coordinates: [-18.7852, 16.2638] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
{
name: "Serengeti",
coordinates: [-2.3333, 34.8333] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
{
name: "Kruger National Park",
coordinates: [-23.9884, 31.5547] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
],
},
]
return (
<Map center={PLACES[0].places[0].coordinates} zoom={3}>
<MapLayers defaultLayerGroups={PLACES.map((place) => place.name)}>
<MapLayersControl />
<MapTileLayer />
<MapTileLayer
name="No Labels"
url="https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png"
darkUrl="https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png"
/>
{PLACES.map((placesGroup) => (
<MapLayerGroup
key={placesGroup.name}
name={placesGroup.name}>
{placesGroup.places.map((place) => (
<MapMarker
key={place.name}
position={place.coordinates}
icon={place.icon}>
<MapPopup className="w-44">
{place.name}
</MapPopup>
</MapMarker>
))}
</MapLayerGroup>
))}
</MapLayers>
</Map>
)
}
Controlling Tile Layers
To allow switching between multiple tile layers, wrap several MapTileLayer
components inside a MapLayers
container and include the MapLayersControl
component to provide the UI for switching between them.
If you want to keep the default tile, you can simply include a <MapTileLayer />
without any props.
import {
Map,
MapLayers,
MapLayersControl,
MapTileLayer,
} from "@/components/ui/map"
import type { LatLngExpression } from "leaflet"
export function MapWithTileLayersControl() {
const TORONTO_COORDINATES = [43.6532, -79.3832] satisfies LatLngExpression
return (
<Map center={TORONTO_COORDINATES} zoom={3}>
<MapLayers defaultTileLayer="National Geographic">
<MapLayersControl />
<MapTileLayer />
<MapTileLayer
name="National Geographic"
url="https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}"
attribution="Tiles © Esri — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC"
/>
<MapTileLayer
name="Satellite"
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
attribution="Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
/>
</MapLayers>
</Map>
)
}
Controlling Layer Groups
To easily show or hide related markers or features on your map, wrap them in MapLayerGroup
components inside a MapLayers
container and include the MapLayersControl
component to provide users with a UI for toggling the visibility of each group.
Use the defaultLayerGroups
prop to specify which layer groups should be visible by default.
import {
Map,
MapLayerGroup,
MapLayers,
MapLayersControl,
MapMarker,
MapTileLayer,
} from "@/components/ui/map"
import type { LatLngExpression } from "leaflet"
import { PawPrintIcon } from "lucide-react"
export function MapWithLayerGroupsControl() {
const PLACES = [
{
name: "Etosha National Park",
coordinates: [-18.7852, 16.2638] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
{
name: "Serengeti",
coordinates: [-2.3333, 34.8333] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
{
name: "Kruger National Park",
coordinates: [-23.9884, 31.5547] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
]
return (
<Map center={PLACES[0].coordinates} zoom={3}>
<MapTileLayer />
<MapLayers defaultLayerGroups={PLACES.map((place) => place.name)}>
<MapLayersControl layerGroupsLabel="Safari" />
{PLACES.map((place) => (
<MapLayerGroup key={place.name} name={place.name}>
<MapMarker
key={place.name}
position={place.coordinates}
icon={place.icon}
/>
</MapLayerGroup>
))}
</MapLayers>
</Map>
)
}
Controlling Feature Groups
MapFeatureGroup
works similarly to MapLayerGroup
.
"use client"
import {
Map,
MapFeatureGroup,
MapLayers,
MapLayersControl,
MapMarker,
MapTileLayer,
} from "@/components/ui/map"
import type { LatLngExpression } from "leaflet"
import { PawPrintIcon } from "lucide-react"
export function MapWithFeatureGroupsControl() {
const PLACES = [
{
name: "Etosha National Park",
coordinates: [-18.7852, 16.2638] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
{
name: "Serengeti",
coordinates: [-2.3333, 34.8333] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
{
name: "Kruger National Park",
coordinates: [-23.9884, 31.5547] satisfies LatLngExpression,
icon: <PawPrintIcon />,
},
]
return (
<Map center={PLACES[0].coordinates} zoom={3}>
<MapTileLayer />
<MapLayers defaultLayerGroups={PLACES.map((place) => place.name)}>
<MapLayersControl layerGroupsLabel="Safari" />
{PLACES.map((place) => (
<MapFeatureGroup
key={place.name}
name={place.name}
eventHandlers={{
click: () =>
console.log("One handler rule them all!"),
}}>
<MapMarker
key={place.name}
position={place.coordinates}
icon={place.icon}
/>
</MapFeatureGroup>
))}
</MapLayers>
</Map>
)
}