RMap

Principal component. All others components described in this library need to be defined as children of this one. This component take his props from MapOptions, map events, and some extra configuration options.

It instantiates a Map object and register event listeners to it. Only a few props are responsive (see reference below).

Hello world.

The RMap component accepts the style and mapStyle props to respectively define the style attribute of the containing element div.maplibregl-map and the style to apply to the Map instance.

import { RMap } from "maplibre-react-components";
 
export default function App() {
  return (
    <RMap
      style={{ minHeight: 200 }}
      mapStyle="https://demotiles.maplibre.org/style.json"
    />
  );
}
 
interface RMapProps {
  style?: CSSProperties;
  mapStyle?: StyleSpecification | string;
  // ...
}

Internally the style prop is wrapped inside a useMemo, you can therefore define this object inline without affecting performance.

// extract of source code : /src/components/RMap/RMap.tsx
const completeStyle: CSSProperties = useMemo(
  () => ({
    position: "relative",
    width: "100%",
    height: "100%",
    ...style,
  }),
  [style],
);

The mapStyle is a reactive value. You can specify the url of a style file or directly an object. If you propose an object be careful to give a stable object for performance reasons the verification is done with a strict equal and not a deepEqual.

MapProps vs MapOptions

You can use all options from the MapLibre MapOptions type as props for the RMap component. Some of these options are reactives and some are not. To distinguish them, non-reactive options have been prefixed with initial and event handlers are prefixed with on. This set constitutes a new type : MapProps (see below for complete reference).

"use client";
 
import { MapLayerMouseEvent } from "maplibre-gl";
import { RMap } from "maplibre-react-components";
 
const center = { lng: 6.6, lat: 46.2 };
 
export default function App() {
  function handleClick(e: MapLayerMouseEvent) {
    console.log(e.lngLat);
  }
 
  return (
    <RMap
      minZoom={8}
      initialCenter={center}
      style={{ minHeight: 300 }}
      onClick={handleClick}
      doubleClickZoom={false}
      mapStyle="https://openmaptiles.geo.data.gouv.fr/styles/osm-bright/style.json"
    />
  );
}
 

For performance reasons, if your prop is an array or an object, define your variable outside your component if it's a readonly value or inside a useMemo to skip re-rendering of components.

You don't need to follow this rule with callbacks (type MapCallbacks). They were implemented following the React principle of useEffectEvents.

Manipulating the map instance

Using the ref prop you get the original MapLibre Map instance. You can also listen to the onMounted event which is called as soon as your map object is accessible.

onMounted is called just after instantiating a Map object while onLoad is called later when the style is loaded.

import { Map } from "maplibre-gl";
import { RMap } from "maplibre-react-components";
import { useRef } from "react";
 
export default function App() {
  const mapRef = useRef<Map>(null);
 
  function handleClick() {
    console.log(mapRef.current);
  }
 
  function handleMounted(map: Map) {
    console.log(map);
  }
 
  return <RMap ref={mapRef} onClick={handleClick} onMounted={handleMounted} />;
}
 

Children components

the RMap component accepts child components.

import { RMap, RNavigationControl } from "maplibre-react-components";
 
export default function App() {
  return (
    <RMap>
      <RNavigationControl position="top-left" />
      <div className="absolute bottom-4 left-4 rounded bg-gray-0 p-4">
        Inlined Control
      </div>
    </RMap>
  );
}
 

Native controls added with their wrapper components (like RNavigationControl) are added in the same place as with the vanilla MapLibre lib. Other components are added as children of the div.maplibregl-children element.

It is not recommended to add content in this way because you can quickly have overlaps with native controls. A best practice is to use the useRControl hook to add components that will not conflict with vanilla content.

<div class="maplibregl-map">
  <div class="maplibregl-canvas-container">
    <canvas class="maplibregl-canvas"></canvas>
  </div>
  <div class="maplibregl-control-container">
    <div class="maplibregl-ctrl-top-left">
      <!-- begin:RNavigationControl -->
      <div class="maplibregl-ctrl maplibregl-ctrl-group">
        <button>+</button>
        <button>-</button>
        <button>rotation</button>
      </div>
      <!-- end:RNavigationControl -->
    </div>
 
    <!-- other positions -->
  </div>
  <div class="maplibregl-children" style="height: 100%">
    <!-- children of RMap are append here -->
    <div class="absolute bottom-4 left-4 rounded-2xl bg-gray-0 p-4">
      Inlined Control
    </div>
  </div>
</div>

Reference

The RMap component accept principally MapProps as props but also some additional options. You will find complete details here.

type RMapProps =
  /**
   * options for the class that controls the map instance, including
   * behavior when changing styles
   */
  ManagerOptions &
 
  /**
   * props of the <div> wrapper component
   */
  RMapComponentProps &
 
  /* props mapped to maplibre MapOptions*/
  MapProps;
type ManagerOptions = {
  mapStyle?: StyleSpecification | string;
  styleDiffing?: boolean;
  styleTransformStyle?: TransformStyleFunction;
  padding?: PaddingOptions;
}
 
type RMapComponentProps = {
  children?: ReactNode;
  style?: CSSProperties;
  id?: string;
  className?: string;
  onMounted?: (map: Map) => void;
}

MapProps type come from the MapLibre MapOptions type. They have been renamed following the conventions described below. Check the MapLibre MapOptions reference page for details of MapInitialOptions and MapReactiveOptions.

type MapProps =
  /**
   * Reactive map options.
   * they have the same name as in MapOptions
   */
  MapReactiveOptions &
 
  /**
   * Reactive map handler options.
   * they have the same name as in MapOptions
   */
  MapHandlerOptions &
 
  /**
   * Listenners for map events
   * prefixed with "on" + first letter uppercase
   */
  MapCallbacks &
 
  /**
   * Non reactive map options (only used during instantiation)
   * prefixed with "initial" + first letter uppercase
   */
  MapInitialOptions;
type MapReactiveOptions = {
  maxBounds?: LngLatBoundsLike;
  minZoom?: number | null;
  maxZoom?: number | null;
  minPitch?: number | null;
  maxPitch?: number | null;
  renderWorldCopies?: boolean;
  pixelRatio?: number;
}
 
type MapHandlerOptions = {
  scrollZoom?: boolean | AroundCenterOptions;
  boxZoom?: boolean;
  dragRotate?: boolean;
  dragPan?: boolean | DragPanOptions;
  keyboard?: boolean;
  doubleClickZoom?: boolean;
  touchZoomRotate?: boolean | AroundCenterOptions;
  touchPitch?: boolean | AroundCenterOptions;
  cooperativeGestures?: GestureOptions;
}
type MapCallbacks = {
  /** Compatible with `layerId` */
  onMouseDown?: (e: MapLayerMouseEvent) => void;
  onMouseUp?: (e: MapLayerMouseEvent) => void;
  onMouseOver?: (e: MapLayerMouseEvent) => void;
  onMouseOut?: (e: MapLayerMouseEvent) => void;
  onMouseMove?: (e: MapLayerMouseEvent) => void;
  onMouseEnter?: (e: MapLayerMouseEvent) => void;
  onMouseLeave?: (e: MapLayerMouseEvent) => void;
  onClick?: (e: MapLayerMouseEvent) => void;
  onDblClick?: (e: MapLayerMouseEvent) => void;
  onContextMenu?: (e: MapLayerMouseEvent) => void;
  onTouchStart?: (e: MapLayerTouchEvent) => void;
  onTouchEnd?: (e: MapLayerTouchEvent) => void;
  onTouchCancel?: (e: MapLayerTouchEvent) => void;
  onTouchMove?: (e: MapLayerTouchEvent) => void;
 
  /** Not compatible with `layerId` */
  onWheel?: (e: MapWheelEvent) => void;
  onResize?: (e: MapLibreEvent) => void;
  onRemove?: (e: MapLibreEvent) => void;
 
  onMoveStart?: (e: MapLibreEvent<MouseEvent | TouchEvent | WheelEvent | undefined>) => void;
  onMove?: (e: MapLibreEvent<MouseEvent | TouchEvent | WheelEvent | undefined>) => void;
  onMoveEnd?: (e: MapLibreEvent<MouseEvent | TouchEvent | WheelEvent | undefined>) => void;
  onDragStart?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onDrag?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onDragEnd?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onZoomStart?: (e: MapLibreEvent<MouseEvent | TouchEvent | WheelEvent | undefined>) => void;
  onZoom?: (e: MapLibreEvent<MouseEvent | TouchEvent | WheelEvent | undefined>) => void;
  onZoomEnd?: (e: MapLibreEvent<MouseEvent | TouchEvent | WheelEvent | undefined>) => void;
  onRotateStart?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onRotate?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onRotateEnd?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onPitchStart?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onPitch?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
  onPitchEnd?: (e: MapLibreEvent<MouseEvent | TouchEvent | undefined>) => void;
 
  onBoxZoomStart?: (e: MapLibreZoomEvent) => void;
  onBoxZoomEnd?: (e: MapLibreZoomEvent) => void;
  onBoxZoomCancel?: (e: MapLibreZoomEvent) => void;
  onWebglContextLost?: (e: MapContextEvent) => void;
  onWebglContextRestored?: (e: MapContextEvent) => void;
  onLoad?: (e: MapLibreEvent) => void;
  onRender?: (e: MapLibreEvent) => void;
  onIdle?: (e: MapLibreEvent) => void;
  onError?: (e: ErrorEvent) => void;
  onData?: (e: MapDataEvent) => void;
  onStyleData?: (e: MapStyleDataEvent) => void;
  onSourceData?: (e: MapSourceDataEvent) => void;
  onDataLoading?: (e: MapDataEvent) => void;
  onStyleDataLoading?: (e: MapStyleDataEvent) => void;
  onSourceDataLoading?: (e: MapSourceDataEvent) => void;
  onTileDataLoading?: (e: MapDataEvent) => void;
  onStyleImageMissing?: (e: MapStyleImageMissingEvent) => void;
  onDataAbort?: (e: MapDataEvent) => void;
  onSourceDataAbort?: (e: MapSourceDataEvent) => void;
  onTerrain?: (e: MapTerrainEvent) => void;
};
type MapInitialOptions = {
  initialHash?: boolean | string;
  initialInteractive?: boolean;
  initialBearingSnap?: number;
  initialAttributionControl?: false | AttributionControlOptions;
  initialMaplibreLogo?: boolean;
  initialLogoPosition?: ControlPosition;
  initialFailIfMajorPerformanceCaveat?: boolean;
  initialPreserveDrawingBuffer?: boolean;
  initialAntialias?: boolean;
  initialRefreshExpiredTiles?: boolean;
  initialTrackResize?: boolean;
  initialMaxTileCacheSize?: number;
  initialMaxTileCacheZoomLevels?: number;
  initialTransformRequest?: RequestTransformFunction;
  initialTransformCameraUpdate?: CameraUpdateTransformFunction;
  initialLocale?: any;
  initialFadeDuration?: number;
  initialCrossSourceCollisions?: boolean;
  initialCollectResourceTiming?: boolean;
  initialClickTolerance?: number;
  initialValidateStyle?: boolean;
  initialMaxCanvasSize?: [number, number];
  initialCancelPendingTileRequestsWhileZooming?: boolean;
  initialCenter?: LngLatLike;
  initialZoom?: number;
  initialBearing?: number;
  initialPitch?: number;
  initialBounds?: LngLatBoundsLike;
  initialFitBoundsOptions?: FitBoundsOptions;
  initialLocalIdeographFontFamily?: string | false;
  initialPitchWithRotate?: boolean;
}