Components
Loading preview...
A MapLibre cluster layer for grouped GeoJSON points with clickable clusters, individual point callbacks, and popup support.
npx shadcn@latest add https://21st.dev/r/mapcn/mapcn-map-cluster-layer"use client";
import { Map, MapClusterLayer, MapPopup, MapControls } from "@/components/ui/mapcn-map-cluster-layer";
import { useState } from "react";
interface EarthquakeProperties {
mag: number;
place: string;
tsunami: number;
}
export default function MapClusterLayerDemo() {
const [selectedPoint, setSelectedPoint] = useState<{ coordinates: [number, number]; properties: EarthquakeProperties } | null>(null);
return (
<div className="flex min-h-screen w-full items-center justify-center bg-background p-8 overflow-hidden">
<div className="h-[420px] w-full max-w-4xl overflow-hidden rounded-xl border shadow-sm">
<Map center={[-103.59, 40.66]} zoom={3.4} fadeDuration={0}>
<MapClusterLayer<EarthquakeProperties> data="https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson" clusterRadius={50} clusterMaxZoom={14} clusterColors={["#1d8cf8", "#6d5dfc", "#e23670"]} pointColor="#1d8cf8" onPointClick={(feature, coordinates) => setSelectedPoint({ coordinates, properties: feature.properties })} />
{selectedPoint && (
<MapPopup key={`${selectedPoint.coordinates[0]}-${selectedPoint.coordinates[1]}`} longitude={selectedPoint.coordinates[0]} latitude={selectedPoint.coordinates[1]} onClose={() => setSelectedPoint(null)} closeOnClick={false} focusAfterOpen={false} closeButton className="w-34">
<div className="text-[13px]">
<p className="text-muted-foreground">Magnitude: <span className="text-foreground font-medium">{selectedPoint.properties.mag}</span></p>
<p className="text-muted-foreground">Tsunami: <span className="text-foreground font-medium">{selectedPoint.properties?.tsunami === 1 ? "Yes" : "No"}</span></p>
</div>
</MapPopup>
)}
<MapControls />
</Map>
</div>
</div>
);
}
export { MapClusterLayerDemo };