Gaurav Singhal Guarav is a data scientist with a strong background in computer science and mathematics. As a developer, he works with Python, Java, Django, HTML, Struts, Hibernate, Vaadin, Web Scrapping, Angular, and React.

Introduction to react-native-maps

5 min read 1644

Having accurate information about your users’ location is a great way to enhance the user experience. For example, you could use this data to show users what is around them, offer exclusive deals for products and services in their area, and much more. Fortunately, if you have a React application, implementing maps is a breeze using the react-native-maps library.

react-native-maps is a component system for maps that ships with platform-native code that needs to be compiled together with React Native. In this guide, we’ll demonstrate how to integrate Google Maps into your React Native application and introduce you to fundamental components such as <MapView /> and <Marker>.

Let’s get started!

Installation

The first thing you need to do is obtain the API key for configuring Google Maps on Android. Once you have the API key, include it in your AndroidManifest.xml file as the child of the <application> element.

<meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="YOUR_API_KEY"/>

Next, run the following command to install react-native-maps in your project.

yarn add react-native-maps -E

Basic use

Remove all the default code from the App.js file and import the <MapView /> component from react-native-maps.

import MapView from "react-native-maps";

Inside the component, render the <MapView /> component.

import React from "react";

import MapView from "react-native-maps";

const App = () => {
  return (
    <MapView
      style={{ flex: 1 }}
      initialRegion={{
        latitude: 37.78825,
        longitude: -122.4324,
        latitudeDelta: 0.05,
        longitudeDelta: 0.05
      }}
    />
  );
};

export default App;

The initialRegion prop shows the region that is to be displayed on the map when the component mounts. The initialRegion value cannot be changed after it has been initialized. Don’t forget to add the style property to specify the dimensions. Otherwise, you’ll end up with a white screen. The value { flex: 1 } will ensure that the <MapView /> takes up the entire screen.

You’ll need to pass an object with the latitude, longitude, and delta values of a region to the initialRegion prop. The latitudeDelta and longitudeDelta properties specify how much the area on the map must be zoomed. To change the region, use the region prop.

Using react-native-maps with the useState hook

To change the region with the state hook, use the onRegionChangeComplete prop to set the new region into the state. The onRegionChangeComplete is a callback prop that is called only once when the region changes.

We made a custom demo for .
No really. Click here to check it out.

import React, { useState } from "react";

import MapView from "react-native-maps";

const App = () => {
  const [region, setRegion] = useState({
    latitude: 51.5079145,
    longitude: -0.0899163,
    latitudeDelta: 0.01,
    longitudeDelta: 0.01
  });

  return (
    <MapView
      style={{ flex: 1 }}
      region={region}
      onRegionChangeComplete={region => setRegion(region)}
    />
  );
};

export default App;

Displaying markers on the map

Start by importing Marker from react-native-maps.

import MapView, { Marker } from "react-native-maps";

Next, render the <Marker /> component as a child of <MapView />. Pass the coordinate for the marker in the coordinate prop.

import React, { useState } from "react";

import MapView, { Marker } from "react-native-maps";

const App = () => {
  const [region, setRegion] = useState({
    latitude: 51.5078788,
    longitude: -0.0877321,
    latitudeDelta: 0.009,
    longitudeDelta: 0.009
  });

  return (
    <MapView
      style={{ flex: 1 }}
      region={region}
      onRegionChangeComplete={region => setRegion(region)}
    >
      <Marker coordinate={{ latitude: 51.5078788, longitude: -0.0877321 }} />
    </MapView>
  );
};

export default App;

The marker should be visible, as shown below.

Marker on a Map

You can add n number of markers to the map and pass it as a direct child to the <MapView /> component.

Custom marker color

To change the color of the marker, use the pinColor prop.

<Marker
  coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}
  pinColor="green"
/>

Custom marker image

You can also add a custom marker image by passing the image prop to the <Marker /> component.

<Marker
  coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}
  image={require("./car.png")}
/>

Custom marker view

The following will display a location with a custom marker view component.

import React, { useState } from "react";
import { View, Text } from "react-native";

import MapView, { Marker } from "react-native-maps";

const CustomMarker = () => (
  <View
    style={{
      paddingVertical: 10,
      paddingHorizontal: 30,
      backgroundColor: "#007bff",
      borderColor: "#eee",
      borderRadius: 5,
      elevation: 10
    }}
  >
    <Text style={{ color: "#fff" }}>Berlin</Text>
  </View>
);

const App = () => {
  const [region, setRegion] = useState({
    latitude: 52.5200066,
    longitude: 13.404954,
    latitudeDelta: 0.005,
    longitudeDelta: 0.005
  });

  return (
    <MapView
      style={{ flex: 1 }}
      region={region}
      onRegionChangeComplete={region => setRegion(region)}
    >
      <Marker coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}>
        <CustomMarker />
      </Marker>
    </MapView>
  );
};

export default App;

Styling the map

Generate the JSON object you’ll use to design the map from the Google style generator.

Next, pass the generated style object to the customMapStyle prop of the <MapView /> component.

import React, { useState } from "react";
import { View, Text } from "react-native";

import MapView, { Marker } from "react-native-maps";

const mapStyle = [
  {
    elementType: "geometry",
    stylers: [
      {
        color: "#1d2c4d"
      }
    ]
  },
  {
    elementType: "labels.text.fill",
    stylers: [
      {
        color: "#8ec3b9"
      }
    ]
  },
  // ...
  {
    featureType: "water",
    elementType: "geometry.fill",
    stylers: [
      {
        color: "#3e73fd"
      }
    ]
  },
  {
    featureType: "water",
    elementType: "labels.text.fill",
    stylers: [
      {
        color: "#4e6d70"
      }
    ]
  }
];

const CustomMarker = () => (
  <View
    style={{
      paddingVertical: 10,
      paddingHorizontal: 30,
      backgroundColor: "#fff",
      borderColor: "#eee",
      borderRadius: 5,
      elevation: 10
    }}
  >
    <Text>Berlin</Text>
  </View>
);

const App = () => {
  const [region, setRegion] = useState({
    latitude: 52.5200066,
    longitude: 13.404954,
    latitudeDelta: 0.005,
    longitudeDelta: 0.005
  });

  return (
    <MapView
      style={{ flex: 1 }}
      region={region}
      onRegionChangeComplete={region => setRegion(region)}
      customMapStyle={mapStyle}
    >
      <Marker coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}>
        <CustomMarker />
      </Marker>
    </MapView>
  );
};

export default App;

You can ignore the mapStyle variable. Since it will be generated from the map style generator, you only need to paste the JSON object into your code and send it to the <MapView /> component.

Custom-Styled Map

As you can see, the map style changed from the default light theme to a customized dark theme.

Animating to a coordinate

What if you want to animate to a particular coordinate?

First, create a ref to <MapView /> using the useRef() hook.

import React, { useState, useRef, useEffect } from "react";
import { View, Text } from "react-native";

import MapView, { Marker } from "react-native-maps";

// ...

const App = () => {
  const _map = useRef(null);

  useEffect(() => {
    // ...
  }, []);

  return (
    <>
      <MapView
        style={{ flex: 1 }}
        ref={_map}
        initialRegion={{
          latitude: 52.5200066,
          longitude: 13.404954,
          latitudeDelta: 0.1,
          longitudeDelta: 0.1
        }}
      >
        <Marker coordinate={{ latitude: 52.5200066, longitude: 13.404954 }}>
          <CustomMarker />
        </Marker>
      </MapView>
    </>
  );
};

export default App;

Next, inside the useEffect() hook, use the animateCamera() function to animate the MapView region.

useEffect(() => {
  if(_map.current) {
    _map.current.animateCamera(
      {
        center: {
          latitude: 50.1109221,
          longitude: 8.6821267
        }
        zoom: 15
      },
      5000
    );
  }
}, []);

The useRef() hook returns a mutable ref object whose current property has the value of the passed argument. If the value of current property is undefined, that means the component is not yet mounted. Now you can access any of the <MapView /> methods using _map.current.

The animateCamera() method accepts an object with center, zoom, heading, and altitude properties as its argument and the animation duration in milliseconds. You can pass the region’s latitude and longitude in the center property, but unlike the initialRegion prop, you cannot give the delta values. The zoom property specifies the extent to which the region needs to be zoomed.

Adding a line to the map

You can use the <Polyline /> component from the react-native-maps library to create lines between multiple coordinates. It accepts an array of coordinates in its coordinates prop. You can also specify additional props for styling purposes, such as strokeWidth, strokeColor, etc.

Let’s create a path between Berlin and Frankfurt.

import React from "react";
import MapView, { Polyline } from "react-native-maps";

const App = () => {
  const Berlin = {
    latitude: 52.5200066,
    longitude: 13.404954
  };

  const Frankfurt = {
    latitude: 50.1109221,
    longitude: 8.6821267
  };

  return (
    <>
      <MapView
        style={{ flex: 1 }}
        initialRegion={{
          latitude: 52.5200066,
          longitude: 13.404954,
          latitudeDelta: 0.1,
          longitudeDelta: 0.1
        }}
      >
        <Polyline coordinates={[Berlin, Franfurt]} />
      </MapView>
    </>
  );
};

export default App;

Store the coordinates for the locations in their respective variables and pass them in an array to the coordinates prop of the <Polyline /> component.

If you look at the results, the line is drawn directly between these coordinates and does not take into account the actual geographical paths and roads. For that, you’ll need to establish multiple coordinates between the source and destination locations by using the Google Maps Direction API, which returns all possible routes between two places.

Create a getDirections function that accepts the coordinates in string format.

const getDirections = async (startLoc, destinationLoc) => {
  try {
    const KEY = "YOUR GOOGLE API KEY";
    let resp = await fetch(
      `https://maps.googleapis.com/maps/api/directions/json?origin=${startLoc}&destination=${destinationLoc}&key=${KEY}`
    );
    let respJson = await resp.json();
    let points = decode(respJson.routes[0].overview_polyline.points);
    console.log(points);
    let coords = points.map((point, index) => {
      return {
        latitude: point[0],
        longitude: point[1]
      };
    });
    return coords;
  } catch (error) {
    return error;
  }
};

Import the decode() function from the @mapbox/polyline library.

import { decode } from "@mapbox/polyline";

The decode() function will convert the encoded polyline points from the overview_polyline.points property into an array containing the latitude and longitude of all the coordinates.

import React, { useState, useEffect } from "react";
import { View, Text } from "react-native";
import MapView, { Polyline } from "react-native-maps";
import { decode } from "@mapbox/polyline";

const getDirections = async (startLoc, destinationLoc) => {
  try {
    const KEY = "YOUR GOOGLE API KEY";
    let resp = await fetch(
      `https://maps.googleapis.com/maps/api/directions/json?origin=${startLoc}&destination=${destinationLoc}&key=${KEY}`
    );
    let respJson = await resp.json();
    let points = decode(respJson.routes[0].overview_polyline.points);
    let coords = points.map((point, index) => {
      return {
        latitude: point[0],
        longitude: point[1]
      };
    });
    return coords;
  } catch (error) {
    return error;
  }
};

const App = () => {
  const [coords, setCoords] = useState([]);

  useEffect(() => {
    getDirections("52.5200066,13.404954", "50.1109221,8.6821267")
      .then(coords => setCoords(coords))
      .catch(err => console.log("Something went wrong"));
  }, []);

  return (
    <>
      <MapView
        style={{ flex: 1 }}
        initialRegion={{
          latitude: 52.5200066,
          longitude: 13.404954,
          latitudeDelta: 0.1,
          longitudeDelta: 0.1
        }}
      >
        {coords.length > 0 && <Polyline coordinates={coords} />}
      </MapView>
    </>
  );
};

export default App;

Conclusion

You should now have a basic understanding of how to implement maps, their benefits, and how to build features on top of the react-native-maps library. You can implement these lessons into your work to build myriad types of applications, from taxi services, to restaurant finders, to delivery apps, and much more. The react-native-maps library makes it simple to integrate maps and is an essential player in the React Native ecosystem.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Gaurav Singhal Guarav is a data scientist with a strong background in computer science and mathematics. As a developer, he works with Python, Java, Django, HTML, Struts, Hibernate, Vaadin, Web Scrapping, Angular, and React.

Leave a Reply