MapLibre needs `window`; Next.js does not. Use a dynamic import with `ssr:false` so the WebGL bundle ships only to the browser while the rest of the page stays server-rendered.
pnpm add maplibre-gl react-map-gl// app/embed/page.tsx
import { NextPage } from 'next';
import dynamic from 'next/dynamic';
import 'maplibre-gl/dist/maplibre-gl.css';
const MAP_STYLE = 'https://maps.guru/api/v1/styles/standard/light/style.json?key=YOUR_API_KEY';
// Critical: ssr:false keeps maplibre-gl out of the server bundle.
const CityMap = dynamic(
() => import('@/components/CityMap').then((mod) => mod.CityMap),
{ ssr: false, loading: () => <div className="map-skeleton" /> }
);
const EmbedPage: NextPage = () => {
return (
<main>
<h1>Delhi NCR live map</h1>
<CityMap />
</main>
);
};
export default EmbedPage;
Free-tier generous enough for real production traffic. 50,000 tile requests/month , no credit card.
maplibre-gl touches window/browser-only APIs at import time. Dynamic-import it with `ssr:false` so the module never executes on the server.
Server actions can read the style URL metadata, but the actual map must run on the client. Use a Server Component to fetch style metadata, pass it down to a Client Component for the WebGL mount.
Yes, the same dynamic-import pattern works in both. Just place the dynamic call at module scope inside the route file.
The vocabulary behind vector tiles, styles, and geocoding, in plain language.
Join developers worldwide who trust maps.guru for their mapping needs. Start free, no credit card required.