MapLibre GL JS touches the DOM and WebGL directly — it has nothing to do on the server. In a Next.js app (App Router), that means a client component, initialized after mount, with proper cleanup on unmount. This guide covers the pitfalls that come up most often.
# npm, pnpm or yarn, whichever your project uses
npm install maplibre-glTwo non-negotiables: the "use client" directive at the top of the file (MapLibre needs window), and the library's CSS imported somewhere — without it, controls and rendering break.
// app/components/Map.js "use client"; import { useEffect, useRef } from "react"; import maplibregl from "maplibre-gl"; import "maplibre-gl/dist/maplibre-gl.css"; export default function Map() { const containerRef = useRef(null); const mapRef = useRef(null); useEffect(() => { // Strict Mode mounts/unmounts effects twice in dev — // this guard stops a second map from being created. if (mapRef.current) return; mapRef.current = new maplibregl.Map({ container: containerRef.current, style: "https://tiles.tilecatcdn.com/styles/default.json?key=YOUR_KEY", center: [2.3522, 48.8566], zoom: 11 ); return () => { mapRef.current?.remove(); mapRef.current = null; }; }, []); return <div ref={containerRef} style={{ height: "100%" }} />; }
MapLibre fills the size of its parent element — if that parent is 0px tall (the default for an empty div with no explicit height), the map stays invisible with no console error. Always give the parent an explicit height:
<div style={{ height: "500px" }}>
<Map />
</div>In development, React 18+ mounts, unmounts and remounts every component to catch unclean side effects. Without the if (mapRef.current) return guard shown above, that initializes two map instances on the same container — classic symptom: duplicated controls, erratic behavior on zoom. It's not a MapLibre bug, it's Strict Mode doing its job; the fix belongs in the component.
new maplibregl.Marker() .setLngLat([2.3522, 48.8566]) .addTo(mapRef.current);
"use client" is mandatory, MapLibre doesn't run on the server.For the full list of endpoints (styles, tiles, sprites, fonts) and per-plan quotas, see the API documentation.