I am currently learning NextJs 14 and would like to use server components as much as possible.
Unfortunately, I have to use "use client"
to use the Maplibre map. At least I have not found any other way. Here is my code, which works so far.
"use client"
import { useEffect } from "react";
import { Map, NavigationControl, GeolocateControl } from "maplibre-gl";
import 'maplibre-gl/dist/maplibre-gl.css';
import { Suspense } from "react";
import { Spinner } from "@/components/spinner";
const CdayMap = () => {
useEffect(() => {
const map = new Map({
container: 'map',
center: [7, 50],
zoom: 14,
style: 'https://tiles.versatiles.org/assets/styles/colorful.json'
});
map.addControl(new NavigationControl());
map.addControl(
new GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
},
trackUserLocation: true,
})
);
}, [])
return (
<Suspense fallback={<Spinner />}>
<div className="flex-1 flex flex-col items-center gap-y-4 animate-fade-from-top">
<div id="map" />
</div>
</Suspense>
);
};
export { CdayMap };
Next, I want to integrate markers and clusters using this tutorial: https://maplibre.org/maplibre-gl-js/docs/examples/cluster/
Here is my code, which unfortunately only shows a few points. I suspect that is because async and await cannot be used in a client component.
"use client"
import { useEffect, useState } from "react";
import maplibregl, { Map, NavigationControl, GeolocateControl } from "maplibre-gl";
import 'maplibre-gl/dist/maplibre-gl.css';
import { Suspense } from "react";
import { Spinner } from "@/components/spinner";
const CdayMap = () => {
const [pageIsMounted, setPageIsMounted] = useState(false)
useEffect(() => {
setPageIsMounted(true)
const map = new Map({
container: 'map',
center: [-103.59179687498357, 40.66995747013945],
zoom: 3,
style: 'https://tiles.versatiles.org/assets/styles/colorful.json'
});
map.addControl(new NavigationControl());
map.addControl(
new GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
},
trackUserLocation: true,
})
);
map.on('load', () => {
map.addSource('earthquakes', {
type: 'geojson',
data: 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson',
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': [
'step',
['get', 'point_count'],
'#51bbd6',
100,
'#f1f075',
750,
'#f28cb1'
],
'circle-radius': [
'step',
['get', 'point_count'],
20,
100,
30,
750,
40
]
}
});
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'earthquakes',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 12
}
});
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'earthquakes',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 4,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
}
});
// inspect a cluster on click
map.on('click', 'clusters', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['clusters']
});
const clusterId = features[0].properties.cluster_id;
const zoom = map.getSource('earthquakes').getClusterExpansionZoom(clusterId);
map.easeTo({
center: features[0].geometry.coordinates,
zoom
});
});
map.on('click', 'unclustered-point', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const mag = e.features[0].properties.mag;
let tsunami;
if (e.features[0].properties.tsunami === 1) {
tsunami = 'yes';
} else {
tsunami = 'no';
}
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(
`magnitude: ${mag}<br>Was there a tsunami?: ${tsunami}`
)
.addTo(map);
});
map.on('mouseenter', 'clusters', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'clusters', () => {
map.getCanvas().style.cursor = '';
});
});
}, [])
return (
<Suspense fallback={<Spinner />}>
<div className="flex-1 flex flex-col items-center gap-y-4 animate-fade-from-top">
<div id="map" />
</div>
</Suspense>
);
};
export { CdayMap };
Is my assumption correct? How can I achieve my goal?