Adding OpenStreetMaps-based Maps to ReactJS app

Leaflet, Mapbox GL/MapLibre GL, OpenLayers in ReactJS
Leaflet, Mapbox GL/MapLibre GL, OpenLayers in ReactJS

If you have already researched how to develop a map with ReactJS, you've probably found that there are a lot of binding libraries. Here are examples of such libraries - react-leaflet or react-mapbox-gl. We don't tell you that those libraries are wrong and will probably help you at the beginning. But wait, do you need them in your application?

In this tutorial, we demonstrate how to use popular map libraries in ReactJS without additional binding libraries. Therefore we show how to integrate React and map libraries like Leaflet, Mapbox GL (MapLibre GL), OpenLayers to create OpenStreetMap-based interactive maps:

We use Geoapify Map Tiles for this tutorial. A Geoapify API Key is necessary to use the Geoapify API. Sign up for an account and generate an API key if you are new to Geoapify.

ReactJS component for an interactive map

All the map libraries are creating a map within a provided container. So let's create a template of a React Functional Component that includes a map container and a callback function to notify when the map is ready:

myMap.jsx
import React, { useEffect, useRef } from 'react';
import './myMap.css';

const MyMap = ({
  mapIsReadyCallback /* To be triggered when a map object is created */,
}) => {
  const mapContainer = useRef(null);

  useEffect(() => {
    console.log('This is called when the component is mounted!');
    mapIsReadyCallback('my map');
  }, [mapContainer.current]);

  return <div className="map-container" ref={mapContainer}></div>;
};

export default MyMap;

We use React.useRef() and React.useEffect() hooks to access DOM element (in the example: map container). The required DOM reference will be accessible inside the effect hook.

myMap.css
.map-container {
    height: 100%;
    width: 100%;
}

Then you can use the component in your application:

App.jsx
import React from 'react';
import './App.scss';
import MyMap from './components/my-map';

function App() {

  const mapIsReadyCallback = (map) => {
    console.log(map);
  };

  return (
    <MyMap mapIsReadyCallback={mapIsReadyCallback}/>
  );
}

export default App;

Note, it may need to set the height to the parent container to make the map visible. This way you can make a fullscreen map when you have only the map on your page.

style.css
html,
body,
#root,
#root > div {
  height: 100%;
  margin: 0;
}

Now let's add a map to our web page with the help of Leaflet, MalLibre GL, and OpenLayers libraries.

React + Leaflet: fast and simple maps

The Leaflet is a great library for building interactive maps. It has excellent documentation and a lot of usage examples. In addition, the Leaflet is open-source and distributed under a commercial-friendly license.

Unfortunately, it doesn't support vector maps out of the box. That's why we use raster map tiles to build a map in this React + Leaflet tutorial.

Step 1. Install Leaflet

You can directly install the Leaflet map library into a React project via NPM:

npm i leaflet

In addition, install the type definitions to use Leaflet's interfaces and types. It's better to install it as a development dependency:

npm i --save-dev @types/leaflet

Step 2. Add Leaflet map styles

To display maps correctly, you need to import '~leaflet/dist/leaflet.css' into your stylesheet:

style.css
@import '~leaflet/dist/leaflet.css';

Step 3. Create a Leaflet map with React

To create a map using Leaflet, simply call the map() method and pass the container DOM element as a parameter. Then add a layer to the map which displays map tiles:

myMap.jsx
import React, { useEffect, useRef } from 'react';
import { map, tileLayer, Browser } from 'leaflet';

import './myMap.css';

const MyMap = ({
  mapIsReadyCallback /* To be triggered when a map object is created */,
}) => {
  const mapContainer = useRef(null);

  useEffect(() => {
    const initialState = {
      lng: 11,
      lat: 49,
      zoom: 4,
    };

    const leafletMap = map(mapContainer.current).setView(
      [initialState.lat, initialState.lng],
      initialState.zoom
    );

    const myAPIKey = 'YOUR_API_KEY';
    const isRetina = Browser.retina;
    var baseUrl = `https://maps.geoapify.com/v1/tile/osm-bright/{z}/{x}/{y}.png?apiKey=${myAPIKey}`;
    var retinaUrl = `https://maps.geoapify.com/v1/tile/osm-bright/{z}/{x}/{y}@2x.png?apiKey=${myAPIKey}`;

    tileLayer(isRetina ? retinaUrl : baseUrl, {
      attribution:
        'Powered by <a href="https://www.geoapify.com/" target="_blank">Geoapify</a> | <a href="https://openmaptiles.org/" rel="nofollow" target="_blank">© OpenMapTiles</a> <a href="https://www.openstreetmap.org/copyright" rel="nofollow" target="_blank">© OpenStreetMap</a> contributors',
      maxZoom: 20,
      id: 'osm-bright',
    }).addTo(leafletMap);

    mapIsReadyCallback(leafletMap);
  }, [mapContainer.current]);

  return <div className="map-container" ref={mapContainer}></div>;
};

export default MyMap;

When working with raster tiles, be aware that you need to perform a little more work than with vector tiles. You need to specify for the tile layer:

  • maximal zoom - Geoapify provides map tiles up to 20th zoom level;
  • correct attribution - you can find detailed information about attribution on the documentation page;
  • high-resolution maps - you need to detect high-resolution screen devices and provide a separate map link for them.

This Stackblitz project is a working application that demonstrates React and Leaflet working together:

React + MapLibre GL (Mapbox GL): customizable vector maps

MapLibre GL is a community-based open-source fork of Mapbox GL library. After Mapbox announced that they would start distributing their software under a proprietary license, the MapLibre GL was created as an independent project to provide free/open-source alternatives to Mapbox's proprietary GL technologies.

MapLibre GL is a map creation library with advanced capabilities and a wide selection of tools for working with maps, geodata, and layers.

MapLibre GL supports Mapbox style specifications and vector maps, making it simple and straightforward to create a map.

Step 1. Install MapLibre GL (Mapbox GL) to React project

In order to add MapLibre GL to a React project, you need to install two packages:

npm i [email protected]
npm i --save-dev @types/geojson

We recommend installing type definition packages as devDependencies.

Step 2. Add MapLibre GL map styles

MapLibre GL is a library for generating maps using the MapLibre CSS styles. You can import the MapLibre CSS styles into a stylesheet file:

style.css
@import '~maplibre-gl/dist/maplibre-gl.css';

Step 3. Create a MapLibre map with React

With MapLibre, you can create a map using just a link to a map style definition and a container element:

myMap.jsx
import maplibregl = require('maplibre-gl');
import React, { useEffect, useRef } from 'react';
import './myMap.css';
import { Map } from 'maplibre-gl';

const MyMap = ({
  mapIsReadyCallback /* To be triggered when a map object is created */,
}) => {
  const mapContainer = useRef(null);

  useEffect(() => {
    const myAPIKey = 'YOUR_API_KEY';
    const mapStyle =
      'https://maps.geoapify.com/v1/styles/dark-matter/style.json';

    const initialState = {
      lng: 11,
      lat: 49,
      zoom: 4,
    };

    const map = new Map({
      container: mapContainer.current,
      style: `${mapStyle}?apiKey=${myAPIKey}`,
      center: [initialState.lng, initialState.lat],
      zoom: initialState.zoom,
    });

    mapIsReadyCallback(map);
  }, [mapContainer.current]);

  return <div className="map-container" ref={mapContainer}></div>;
};

export default MyMap;

The style.json file contains all the information required to render a map, including the maximal zoom level and attributions.

We used the dark-matter map style for this mapbox + react example. We used the dark-matter map style for this mapbox + react example. You can find more map styles on the Map Tiles Documentation page.

This Stackblitz project is a working application that demonstrates React and MapLibre GL working together:

React + OpenLayers: more expert tools for your map

OpenLayers is an open-source website that offers more features for building complex maps. It often tends to show itself as less straightforward, but its flexibility serves its purpose to create more complex and expert map applications.

Step 1. Install OpenLayers and its type definitions

Like other libraries, you need to install the OpenLayers library itself and its type definitions to use the OpenLayers in your React project:

npm i ol
npm i --save-dev @types/openlayers

The TypeScript type definitions are installed into the development dependencies by using the --save-dev flag.

Step 2. Add OpenLayers styling

The ol.css contains the styling required to display a map correctly. Import it into your stylesheet file:

style.css
'@import '~ol/ol.css';

Step 3. Create an OpenLayers map with React

You can create a map by providing a link to a styles.json file in the ol-mapbox-style plugin:

myMap.jsx
import React, { useEffect, useRef } from 'react';
import './myMap.css';
import { Map, View } from 'ol';
import FullScreen from 'ol/control/FullScreen';
import olms from 'ol-mapbox-style';
import { transform } from 'ol/proj';

const MyMap = ({
  mapIsReadyCallback /* To be triggered when a map object is created */,
}) => {
  const mapContainer = useRef(null);

  useEffect(() => {
    const initialState = {
      lng: 11,
      lat: 49,
      zoom: 4,
    };

    const myAPIKey = 'YOUR_API_KEY';
    const mapStyle = 'https://maps.geoapify.com/v1/styles/positron/style.json';

    olms(mapContainer.current, `${mapStyle}?apiKey=${myAPIKey}`).then((map) => {
      map
        .getView()
        .setCenter(
          transform(
            [initialState.lng, initialState.lat],
            'EPSG:4326',
            'EPSG:3857'
          )
        );
      map.getView().setZoom(initialState.zoom);

      mapIsReadyCallback(map);
    });
  }, [mapContainer.current]);

  return <div className="map-container" ref={mapContainer}></div>;
};

export default MyMap;

OpenLayers Map view uses Web Mercator ( EPSG:3857 ) as its default projection. However, you can transform Latitude/Longitude coordinates, which is World Geodetic System 1984 ( EPSG:4326 ), to the Web Mercator projection by using the transform() method.

This Stackblitz project is a working application that demonstrates React and OpenLayers working together:

What's next