TileCatTileCat
FeaturesDocsPricing
FREN

Integrate MapLibre GL JS with Next.js

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.

1. Install the dependency

# npm, pnpm or yarn, whichever your project uses
npm install maplibre-gl

2. The map component

Two 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%" }} />;
}

3. Pitfall #1: container height

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>

4. Pitfall #2: React Strict Mode

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.

5. Adding a marker

new maplibregl.Marker()
  .setLngLat([2.3522, 48.8566])
  .addTo(mapRef.current);

6. Recap

  • "use client" is mandatory, MapLibre doesn't run on the server.
  • Import maplibre-gl's CSS or rendering breaks.
  • Always give the map container an explicit height.
  • Keep a ref to the instance and clean it up on unmount to survive Strict Mode.

For the full list of endpoints (styles, tiles, sprites, fonts) and per-plan quotas, see the API documentation.

Get a free API key

TileCat
© 2026 TileCat — OpenStreetMap basemaps
Legal noticeTerms of salePrivacyStatus