Location-based features have become an essential part of modern mobile applications — from ride-hailing and delivery apps to social platforms and travel services. Whether you’re displaying nearby points of interest, tracking movement in real time, or showing directions between two coordinates, React Native provides powerful tools to integrate maps and geolocation into your applications.
In this tutorial, you’ll learn everything you need to build map-powered mobile apps using React Native, including:
-
Installing and configuring React Native Maps
-
Choosing the best geolocation library (Expo or Bare RN)
-
Requesting and handling location permissions
-
Displaying interactive maps
-
Showing the user’s current location
-
Adding markers and callouts
-
Drawing routes with polylines
-
Implementing live location tracking
-
Understanding background location considerations
-
Avoiding common pitfalls on Android & iOS
We will cover both Expo and Bare React Native workflows, so you can confidently build location features no matter which environment you prefer.
By the end of this guide, you will have a fully working map application that displays the user’s current location and allows you to add your own map features with ease.
Prerequisites
Before building map and location features in React Native, make sure your development environment is properly set up. Here’s what you’ll need:
1. Node.js and Package Manager
Install the latest LTS version of Node.js, which includes npm.
You may also use Yarn or pnpm depending on your preference.
Check your versions:
node -v
npm -v
2. React Native Development Environment
This tutorial supports two workflows:
-
Expo Managed Workflow
Ideal for beginners, rapid prototyping, and simpler projects.
Requires:npm install -g expo-cli -
Bare React Native Workflow
Recommended for advanced or production-ready apps that need native modules and customizations.
If you're using Expo
-
Install the Expo CLI.
-
An Android/iOS simulator or Expo Go app on your device.
If you're using Bare React Native
Make sure you’ve installed:
-
Android Studio (SDK + Emulator)
-
Xcode (for iOS development, macOS only)
-
CocoaPods (for iOS dependencies)
3. Android & iOS Setup Requirements
Working with maps requires some platform-level setup:
Android Requirements
-
Google Play Services installed on emulator/device
-
A Google Maps API Key (we’ll configure this later)
iOS Requirements
-
Minimum supported version: iOS 11 (recommended: iOS 12+)
-
CocoaPods installed:
sudo gem install cocoapods
4. Basic JavaScript & React Native Knowledge
You should be comfortable with:
-
React hooks (
useState,useEffect) -
Components & props
-
Basic navigation (optional)
Nothing too advanced — we will guide you step-by-step.
Choosing the Right Packages (Expo vs Bare React Native)
React Native offers multiple options when working with maps and geolocation. The package you choose will depend on whether you’re building with Expo or using the Bare React Native CLI.
In this section, we’ll break down the recommended packages for each workflow and explain when to choose each one.
1. Mapping Library: react-native-maps
No matter which workflow you use, the primary library for displaying maps in React Native is:
react-native-maps
This library supports:
-
Google Maps (Android & iOS)
-
Apple Maps (iOS)
-
Marker & Callout components
-
Polylines, Circles, Polygons
-
Custom map styling
-
Map events (region change, tap, long press, etc.)
It is widely used, actively maintained, and compatible with both Expo and Bare React Native.
2. Geolocation Options
React Native does not include built-in geolocation support in newer versions. You must install a separate geolocation package.
Here are the best options:
A. Expo (Managed Workflow)
Use:
-
expo-location
Provides:-
Current location
-
Continuous location updates
-
Geofencing (limited)
-
Reverse geocoding
-
Permission handling
-
-
react-native-maps
Supports:-
Standard and Google-provided maps
-
Full compatibility with Expo
-
Recommended for beginners or lightweight apps.
B. Bare React Native Workflow
You have two strong options:
-
react-native-geolocation-service(Recommended)-
Highly accurate
-
Supports background mode with optional configuration
-
More reliable on Android devices
-
Works well with Google Play Services
-
Supports newer React Native architectures
-
-
@react-native-community/geolocation-
Community-driven
-
Good for basic foreground needs
-
If you need:
-
Background tracking
-
High accuracy
-
Production-grade movement tracking
→ Usereact-native-geolocation-service.
3. Summary: Which One Should You Choose?
| Workflow | Maps Library | Geolocation Library | Difficulty | Best For |
|---|---|---|---|---|
| Expo Managed | react-native-maps |
expo-location |
★☆☆ (Easy) | Beginners, rapid prototyping |
| Bare RN | react-native-maps |
react-native-geolocation-service |
★★☆ (Moderate) | Production apps needing accuracy |
| Bare RN (Simple) | react-native-maps |
@react-native-community/geolocation |
★★☆ | Basic apps without heavy location requirements |
4. What We Will Use in This Tutorial
To ensure every developer can follow along, this tutorial will include both approaches:
-
Expo Version (for fast setup)
-
Bare React Native Version (for deeper native-level control)
Each main section will include commands and code snippets for both where applicable.
Installing React Native Maps & Geolocation
In this section, we’ll set up the required packages for both Expo and Bare React Native environments. Follow the path that matches your workflow. After installation, we’ll handle platform-specific configuration for Android and iOS.
1. Installing in Expo (Managed Workflow)
Expo makes installation straightforward since native dependencies are precompiled.
Step 1: Create a New Expo Project
npx create-expo-app rn-maps-demo
cd rn-maps-demo
Step 2: Install Map and Location Packages
npx expo install react-native-maps expo-location
Expo will automatically install versions compatible with your current Expo SDK.
2. Installing in Bare React Native
If you're using the React Native CLI, you must install all native modules manually and run pod install for iOS.
Step 1: Create a New Project
npx @react-native-community/cli init RNMapsDemo
cd RNMapsDemo
Step 2: Install Required Packages
Install the map library:
yarn add react-native-maps
Install geolocation library (recommended):
yarn add react-native-geolocation-service react-native-permissions
or, for a simpler setup:
yarn add @react-native-community/geolocation
Step 3: Install iOS Pods
If you’re on macOS:
cd ios && pod install && cd ..
This ensures native dependencies like GoogleMaps and CoreLocation are properly linked.
3. Android Configuration (Bare React Native Only)
Expo does this automatically — skip if using Expo.
Step 1: Add Google Maps API Key
Open:
android/app/src/main/AndroidManifest.xml
Add inside the <application> tag:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_GOOGLE_MAPS_API_KEY"/>
Ensure that you replace the placeholder with a valid API key.
Step 2: Add Required Permissions
Inside the same file:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
If you plan to support background tracking:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
4. iOS Configuration (Bare React Native Only)
Step 1: Add Location Permission Messages
Open:
ios/RNMapsDemo/Info.plist
Add:
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app requires your location to display it on the map.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location to track movement even in background.</string>
At a minimum, NSLocationWhenInUseUsageDescription is required.
Step 2: Configure Google Maps (Optional)
If you want Google Maps on iOS instead of Apple Maps:
-
Open
AppDelegate.morAppDelegate.mm. -
Add:
#import <GoogleMaps/GoogleMaps.h>
Add in didFinishLaunchingWithOptions:
[GMSServices provideAPIKey:@"YOUR_GOOGLE_MAPS_API_KEY"];
5. Verifying the Setup
Run your project:
Expo:
npx expo start
Use:
-
Expo Go app
-
Web
-
iOS simulator
-
Android emulator
Bare React Native:
npx react-native run-android
# or
npx react-native run-ios
If the app builds and launches successfully, you're ready to move on to showing your first map.
Displaying Your First Map (MapView Basics)
With all dependencies installed and your environment configured, it’s time to render your first map. In this section, you’ll learn how to:
-
Import and use the
MapViewcomponent -
Set an initial region (latitude, longitude, zoom)
-
Display a full-screen map
-
Use a simple marker
We’ll provide examples for both Expo and Bare React Native, but the code is nearly identical.
1. Understanding the MapView Component
MapView is the core component of react-native-maps. It supports:
-
Standard map view
-
Google Maps provider
-
Custom coordinates
-
User location display
-
Zoom, tilt, scroll interactions
-
Map events (onPress, onRegionChange, etc.)
The most important props are:
| Prop | Description |
|---|---|
initialRegion |
Sets the map's initial location and zoom |
region |
Makes the map controlled (syncs latitude/longitude with state) |
showsUserLocation |
Enables the blue dot for user location |
provider |
Choose Google Maps explicitly (PROVIDER_GOOGLE) |
2. Creating a Simple Full-Screen Map
Create a new screen or edit App.tsx.
Example (Works in Both Expo & Bare RN)
import React from "react";
import { StyleSheet, View } from "react-native";
import MapView, { Region } from "react-native-maps";
export default function App() {
const initialRegion: Region = {
latitude: -6.200000,
longitude: 106.816666,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
};
return (
<View style={styles.container}>
<MapView
style={styles.map}
initialRegion={initialRegion}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
map: { flex: 1 },
});
3. Using Google Maps Provider (Optional)
By default:
-
Android → Google Maps
-
iOS → Apple Maps
If you want to force Google Maps on iOS:
import { PROVIDER_GOOGLE } from 'react-native-maps';
<MapView
provider={PROVIDER_GOOGLE}
style={styles.map}
initialRegion={initialRegion}
/>
You must have configured your iOS AppDelegate with a Google Maps API key (covered in Section 4).
4. Adding a Simple Marker
Let’s drop a pin at a specific coordinate.
import MapView, { Marker } from 'react-native-maps';
<MapView style={styles.map} initialRegion={initialRegion}>
<Marker
coordinate={{ latitude: -6.200000, longitude: 106.816666 }}
title="Jakarta"
description="Capital of Indonesia"
/>
</MapView>
You should now see a pin on the map with a callout when tapped.
5. Handling Basic Map Events
You can capture user interactions:
Tap Event
<MapView
style={styles.map}
initialRegion={initialRegion}
onPress={(e) => {
const { latitude, longitude } = e.nativeEvent.coordinate;
console.log("Map pressed at:", latitude, longitude);
}}
/>
This will log coordinates where the user taps.
6. Using a Controlled Region (Optional)
If you want to move the map programmatically:
const [region, setRegion] = useState(initialRegion);
<MapView
style={styles.map}
region={region}
onRegionChangeComplete={setRegion}
/>
This makes the map behave like a controlled component in React.
You now have a fully working map on your screen.
Showing the User’s Current Location (TSX)
Displaying the user’s current position is one of the most common requirements for map-based apps. In React Native, you’ll typically do this by:
-
Requesting location permissions
-
Getting the user’s coordinates
-
Updating the map region
-
Enabling the built-in blue location dot (optional)
In this section, you’ll learn how to do this in both Expo (expo-location) and Bare React Native (react-native-geolocation-service) — all in TypeScript (.tsx).
1. Using Expo (expo-location) — TypeScript Example
Expo provides the simplest way to access the device’s location.
Installation Reminder (if needed)
npx expo install expo-location react-native-maps
Full Example: Get and Show Current User Location
Create or edit App.tsx:
import React, { useEffect, useState } from "react";
import { StyleSheet, View, ActivityIndicator, Text } from "react-native";
import MapView, { Marker, Region } from "react-native-maps";
import * as Location from "expo-location";
export default function App() {
const [region, setRegion] = useState<Region | null>(null);
const [errorMsg, setErrorMsg] = useState<string | null>(null);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
setErrorMsg("Permission to access location was denied");
return;
}
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
setRegion({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
})();
}, []);
if (errorMsg) {
return (
<View style={styles.center}>
<Text>{errorMsg}</Text>
</View>
);
}
if (!region) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView
style={styles.map}
initialRegion={region}
showsUserLocation={true}
>
<Marker coordinate={region} title="You are here" />
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: "center", alignItems: "center" },
});
What this does:
✔ Requests permission
✔ Gets current GPS coordinates
✔ Moves the map to the user’s location
✔ Shows a custom marker AND the native blue dot
2. Using Bare React Native (react-native-geolocation-service) — TypeScript Example
For non-Expo apps, react-native-geolocation-service is the most reliable option.
Installation Reminder
yarn add react-native-geolocation-service react-native-permissions
cd ios && pod install && cd ..
You MUST request permissions manually (as shown in Section 4).
3. Full Example (TSX): Getting User Location
Create App.tsx:
import React, { useEffect, useState } from "react";
import { PermissionsAndroid, Platform, StyleSheet, View, ActivityIndicator, Text } from "react-native";
import MapView, { Marker, Region } from "react-native-maps";
import Geolocation, { GeoPosition } from "react-native-geolocation-service";
export default function App() {
const [region, setRegion] = useState<Region | null>(null);
const [errorMsg, setErrorMsg] = useState<string | null>(null);
const requestPermission = async (): Promise<boolean> => {
if (Platform.OS === "ios") {
return true; // iOS handled via Info.plist
}
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
};
useEffect(() => {
(async () => {
const hasPermission = await requestPermission();
if (!hasPermission) {
setErrorMsg("Location permission denied");
return;
}
Geolocation.getCurrentPosition(
(pos: GeoPosition) => {
const { latitude, longitude } = pos.coords;
setRegion({
latitude,
longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
},
(error) => setErrorMsg(error.message),
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
);
})();
}, []);
if (errorMsg) {
return (
<View style={styles.center}>
<Text>{errorMsg}</Text>
</View>
);
}
if (!region) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView
style={styles.map}
initialRegion={region}
showsUserLocation={true}
>
<Marker coordinate={region} title="You are here" />
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: "center", alignItems: "center" },
});
4. Showing User Location with the Native Blue Dot
The following works in both Expo and Bare RN:
<MapView
style={styles.map}
showsUserLocation={true}
followsUserLocation={true} // optional: auto-center on user
/>
5. Tip: Extracting Logic Into a Custom Hook (Recommended)
To keep components clean, you can create a custom hook:
/src/hooks/useUserLocation.ts
import { useEffect, useState } from "react";
import * as Location from "expo-location";
import { Region } from "react-native-maps";
export const useUserLocation = () => {
const [region, setRegion] = useState<Region | null>(null);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") return;
const loc = await Location.getCurrentPositionAsync({});
setRegion({
latitude: loc.coords.latitude,
longitude: loc.coords.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
})();
}, []);
return region;
};
Then use:
const region = useUserLocation();
Cleaner, reusable, and scalable.
Summary
You learned how to:
-
Request location permissions (Expo + Bare RN)
-
Get current location in TypeScript
-
Display a map centered on the user
-
Show a marker and the blue location dot
-
Implement optional tracking and hooks
Markers, Callouts, and Clustering (TSX)
Now that your map is rendering and centered on the user’s location, the next step is to add markers, callouts, and optionally marker clustering. These features let you display points of interest, show custom icons, and manage large datasets efficiently.
All examples in this section are written in TypeScript (.tsx) and work in both Expo and Bare React Native unless otherwise stated.
1. Adding Basic Markers (TSX)
Markers represent a single point on the map.
Example: A Simple Marker
import MapView, { Marker } from "react-native-maps";
<MapView style={{ flex: 1 }} initialRegion={initialRegion}>
<Marker
coordinate={{ latitude: -6.2, longitude: 106.816666 }}
title="Jakarta"
description="Capital of Indonesia"
/>
</MapView>
Marker Properties:
-
coordinate: latitude + longitude -
title: callout title -
description: callout subtitle -
pinColor: change pin color -
image: custom marker icon -
draggable: allow marker dragging
2. Using a Custom Marker Icon
You can replace the default pin with your own image.
Example structure:
/assets/
custom-pin.png
TSX Example:
<Marker
coordinate={{ latitude: -6.2, longitude: 106.816666 }}
title="Custom Pin"
image={require("../assets/custom-pin.png")}
/>
Make sure your image is a small PNG (48x48 or 64x64 recommended).
3. Interactive Callouts (TSX)
Callouts appear when a marker is tapped. You can fully customize them.
Example: Custom Callout Content
import { Callout } from "react-native-maps";
import { View, Text, StyleSheet } from "react-native";
<Marker coordinate={{ latitude: -6.2, longitude: 106.816666 }}>
<Callout tooltip>
<View style={styles.callout}>
<Text style={styles.title}>Jakarta</Text>
<Text style={styles.subtitle}>Capital of Indonesia</Text>
</View>
</Callout>
</Marker>
Styles:
const styles = StyleSheet.create({
callout: {
backgroundColor: "#fff",
padding: 10,
borderRadius: 8,
elevation: 4,
},
title: {
fontWeight: "bold",
marginBottom: 4,
},
subtitle: {
color: "#555",
},
});
Using tooltip gives a cleaner pop-up without the default bubble pointer.
4. Multiple Markers from an Array
In real apps, you’ll load pin data from APIs or local files.
TypeScript Interface:
interface Place {
id: string;
title: string;
latitude: number;
longitude: number;
}
Marker List Example:
const places: Place[] = [
{ id: "1", title: "Monas", latitude: -6.1754, longitude: 106.8272 },
{ id: "2", title: "Kota Tua", latitude: -6.1352, longitude: 106.8133 },
];
<MapView style={{ flex: 1 }} initialRegion={initialRegion}>
{places.map((place) => (
<Marker
key={place.id}
coordinate={{
latitude: place.latitude,
longitude: place.longitude,
}}
title={place.title}
/>
))}
</MapView>
5. Draggable Markers
Useful for allowing users to select a location (e.g., choosing a delivery drop-off).
<Marker
coordinate={markerPosition}
draggable
onDragEnd={(e) => {
const { latitude, longitude } = e.nativeEvent.coordinate;
setMarkerPosition({ latitude, longitude });
}}
/>
6. Marker Clustering (Highly Recommended for Many Points)
When you have 50+ markers, clustering is essential for performance and usability.
The most popular library is:
react-native-map-clustering
Installation
Expo:
npx expo install react-native-map-clustering
Bare RN:
yarn add react-native-map-clustering
cd ios && pod install && cd ..
Usage (TSX)
import MapViewCluster from "react-native-map-clustering";
import { Marker } from "react-native-maps";
<MapViewCluster
style={{ flex: 1 }}
initialRegion={initialRegion}
radius={40} // cluster radius
>
{places.map((place) => (
<Marker
key={place.id}
coordinate={{
latitude: place.latitude,
longitude: place.longitude,
}}
title={place.title}
/>
))}
</MapViewCluster>
Features:
✔ Automatic clustering
✔ Smooth animations
✔ Configurable cluster size and appearance
✔ Works with TypeScript
7. Custom Cluster Rendering
If you want to style the cluster icons:
<MapViewCluster
style={{ flex: 1 }}
initialRegion={initialRegion}
renderCluster={(cluster) => (
<Marker coordinate={cluster.geometry.coordinates}>
<View style={styles.cluster}>
<Text style={styles.clusterText}>{cluster.pointCount}</Text>
</View>
</Marker>
)}
>
{/* markers */}
</MapViewCluster>
Styles:
const styles = StyleSheet.create({
cluster: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: "#ff5722",
justifyContent: "center",
alignItems: "center",
},
clusterText: {
color: "#fff",
fontWeight: "bold",
},
});
Summary
You learned how to:
-
Add markers (simple + custom icon)
-
Build custom callouts
-
Render multiple markers from an array
-
Drag markers to update coordinates
-
Use clustering to handle large datasets
Your map is now more interactive and ready for advanced features like routing or live tracking.
Drawing Routes with Polylines (TSX)
Routes are essential in many location-based apps — whether you're showing directions, mapping a delivery path, visualizing user movement, or connecting multiple points of interest.
In React Native Maps, routes are drawn using the Polyline component. With TypeScript, you’ll benefit from strong coordinate typing and safer rendering.
This section covers:
-
How to draw a simple polyline
-
Styling your polyline
-
Connecting multiple locations
-
Using Google Directions API to draw real routes
-
Decoding Google polyline strings
-
Rendering the final route on the map
Everything is written in TypeScript (.tsx) and works in both Expo and Bare RN.
1. Importing and Using Polyline
Polyline is included in react-native-maps.
Basic TSX imports
import MapView, { Polyline, LatLng, Region } from "react-native-maps";
2. Drawing a Simple Polyline Between Two Points
TypeScript Example
const points: LatLng[] = [
{ latitude: -6.1754, longitude: 106.8272 }, // Monas
{ latitude: -6.1352, longitude: 106.8133 }, // Kota Tua
];
<MapView style={{ flex: 1 }} initialRegion={initialRegion}>
<Polyline
coordinates={points}
strokeWidth={4}
strokeColor="#FF0000"
/>
</MapView>
Notes:
-
coordinatesmust be an array of{ latitude: number; longitude: number } -
strokeWidthdetermines thickness -
strokeColorsets line color
3. Drawing More Complex Routes (Multiple Points)
You can connect multiple points easily:
const routeCoordinates: LatLng[] = [
{ latitude: -6.2, longitude: 106.8 },
{ latitude: -6.21, longitude: 106.82 },
{ latitude: -6.23, longitude: 106.85 },
{ latitude: -6.25, longitude: 106.87 },
];
<Polyline
coordinates={routeCoordinates}
strokeWidth={5}
strokeColor="#1E90FF"
/>
4. Styling Your Polyline
Available Styling Props
| Prop | Description |
|---|---|
strokeColor |
Line color |
strokeWidth |
Line thickness |
lineDashPattern |
Dotted or dashed patterns |
lineCap |
Shape of line ends (butt / round / square) |
geodesic |
Makes the line follow the curvature of Earth |
Example: Dashed Route
<Polyline
coordinates={routeCoordinates}
strokeWidth={4}
strokeColor="#4CAF50"
lineDashPattern={[10, 5]} // dash 10px, gap 5px
/>
5. Drawing Real Directions Using Google Directions API
This is the most powerful and widely used approach.
Step 1: Build a Directions API URL
Replace YOUR_API_KEY with your real Google Maps API key.
const buildDirectionsUrl = (
origin: LatLng,
destination: LatLng
) =>
`https://maps.googleapis.com/maps/api/directions/json?origin=${origin.latitude},${origin.longitude}&destination=${destination.latitude},${destination.longitude}&key=YOUR_API_KEY`;
6. Decoding Google's Encoded Polyline
Google returns routes as encoded strings. Use a decoder to convert them into coordinates.
Install the decoder:
yarn add @mapbox/polyline
Import:
import polyline from "@mapbox/polyline";
7. Full Example: Fetch & Draw Google Directions Route (TSX)
import React, { useEffect, useState } from "react";
import { View, StyleSheet, ActivityIndicator } from "react-native";
import MapView, { Polyline, LatLng, Region } from "react-native-maps";
import polyline from "@mapbox/polyline";
const origin: LatLng = { latitude: -6.1754, longitude: 106.8272 }; // Monas
const destination: LatLng = { latitude: -6.1352, longitude: 106.8133 }; // Kota Tua
export default function DirectionsExample() {
const [routeCoords, setRouteCoords] = useState<LatLng[]>([]);
const [loading, setLoading] = useState(true);
const initialRegion: Region = {
latitude: origin.latitude,
longitude: origin.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05,
};
useEffect(() => {
async function fetchRoute() {
try {
const url = `https://maps.googleapis.com/maps/api/directions/json?origin=${origin.latitude},${origin.longitude}&destination=${destination.latitude},${destination.longitude}&key=YOUR_API_KEY`;
const response = await fetch(url);
const json = await response.json();
if (!json.routes.length) return;
// Decode polyline
const points = polyline.decode(
json.routes[0].overview_polyline.points
);
const coords: LatLng[] = points.map(([lat, lng]) => ({
latitude: lat,
longitude: lng,
}));
setRouteCoords(coords);
} catch (error) {
console.error("Directions error:", error);
} finally {
setLoading(false);
}
}
fetchRoute();
}, []);
if (loading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView style={styles.map} initialRegion={initialRegion}>
<Polyline
coordinates={routeCoords}
strokeWidth={5}
strokeColor="#2196F3"
/>
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: "center", alignItems: "center" },
});
8. Drawing Real-Time Movement (e.g., Delivery Apps)
If you’re tracking a moving user/device:
-
Continuously update the polyline with new coordinates
-
Append incoming GPS points into an array
-
Re-render the
Polyline
Minimal Example (TSX):
const [path, setPath] = useState<LatLng[]>([]);
useEffect(() => {
const watchId = Geolocation.watchPosition(
(position) => {
const { latitude, longitude } = position.coords;
setPath((prev) => [...prev, { latitude, longitude }]);
},
console.log,
{ enableHighAccuracy: true, distanceFilter: 5 }
);
return () => Geolocation.clearWatch(watchId);
}, []);
Then render:
<Polyline coordinates={path} strokeWidth={4} strokeColor="#FF5722" />
Perfect for:
-
Jogging apps
-
Delivery/tracking apps
-
Vehicle monitoring
Summary
You now know how to:
-
Draw simple and complex polylines
-
Style routes with color, width, and dash patterns
-
Fetch and decode Google Directions API routes
-
Render real-time movement routes
Your map can now display rich navigation features.
Real-Time Location Updates & Background Tracking (TSX)
In many real-world apps—delivery tracking, fitness tracking, transportation, on-demand services—you need continuous, real-time, and sometimes background location updates. This section teaches you how to:
-
Watch the user’s location in real-time
-
Track user movement and update polyline routes
-
Handle battery-efficient tracking
-
Enable background location (Expo & Bare RN)
-
Understand platform limitations (iOS vs Android)
All examples are written in TypeScript (.tsx) and compatible with both Expo and Bare React Native.
1. Real-Time Location Updates (Foreground Tracking)
Real-time tracking means listening to location changes using either:
-
Expo
-
Location.watchPositionAsync()
-
-
Bare RN
-
Geolocation.watchPosition()
-
Below are TSX examples for both.
2. Real-Time Tracking in Expo (TSX)
Example: Watch and Display User Movement on Map
import React, { useEffect, useState } from "react";
import { StyleSheet, View, ActivityIndicator } from "react-native";
import MapView, { Polyline, Marker, LatLng, Region } from "react-native-maps";
import * as Location from "expo-location";
export default function LiveTracking() {
const [path, setPath] = useState<LatLng[]>([]);
const [region, setRegion] = useState<Region | null>(null);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") return;
const subscription = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
timeInterval: 2000, // update every 2 seconds
distanceInterval: 5, // or every 5 meters
},
(location) => {
const newPoint = {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
};
setPath((prev) => [...prev, newPoint]);
if (!region) {
setRegion({
latitude: newPoint.latitude,
longitude: newPoint.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
}
}
);
return () => subscription.remove();
})();
}, []);
if (!region) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView style={styles.map} initialRegion={region} showsUserLocation={true}>
<Polyline coordinates={path} strokeWidth={4} strokeColor="#e91e63" />
{path.length > 0 && <Marker coordinate={path[path.length - 1]} title="Current Position" />}
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: "center", alignItems: "center" },
});
✔ Tracks movement
✔ Builds a live polyline path
✔ Maps auto-updates the user position
3. Real-Time Tracking in Bare React Native (react-native-geolocation-service)
TSX Example
import React, { useEffect, useState } from "react";
import { StyleSheet, View, ActivityIndicator } from "react-native";
import MapView, { Polyline, Marker, LatLng, Region } from "react-native-maps";
import Geolocation, { GeoPosition } from "react-native-geolocation-service";
export default function LiveTracking() {
const [path, setPath] = useState<LatLng[]>([]);
const [region, setRegion] = useState<Region | null>(null);
useEffect(() => {
const watchId = Geolocation.watchPosition(
(pos: GeoPosition) => {
const { latitude, longitude } = pos.coords;
const newPoint = { latitude, longitude };
setPath((prev) => [...prev, newPoint]);
if (!region) {
setRegion({
latitude,
longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
}
},
console.warn,
{
enableHighAccuracy: true,
distanceFilter: 5,
interval: 3000,
fastestInterval: 2000,
}
);
return () => Geolocation.clearWatch(watchId);
}, []);
if (!region) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView style={styles.map} initialRegion={region} showsUserLocation={true}>
<Polyline coordinates={path} strokeWidth={4} strokeColor="#2196f3" />
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: "center", alignItems: "center" },
});
✔ Best accuracy
✔ Works extremely well on Android
✔ Supports background tracking with additional setup
4. Improving Accuracy & Battery Efficiency
Recommended tracking settings:
High accuracy (more battery):
enableHighAccuracy: true,
distanceInterval: 1,
Balanced (recommended for delivery apps):
enableHighAccuracy: true,
distanceInterval: 5,
Battery saver mode:
5. Background Location (IMPORTANT)
Background location allows:
-
Tracking movement when the app is minimized
-
Delivery drivers to keep the route updated
-
Fitness apps to log running while the screen is off
But…
⚠️ Background location is heavily restricted by both iOS and Android.
⚠️ You MUST declare permissions properly, or your app may be rejected.
6. Background Tracking in Expo
Expo supports background location using:
import * as TaskManager from "expo-task-manager";
import * as Location from "expo-location";
Step 1: Define a Background Task
TaskManager.defineTask("location-tracking", ({ data, error }) => {
if (error) return;
const { locations } = data as any;
console.log("Background location:", locations[0]);
});
Step 2: Start Background Updates
await Location.startLocationUpdatesAsync("location-tracking", {
accuracy: Location.Accuracy.Highest,
distanceInterval: 5,
showsBackgroundLocationIndicator: true,
foregroundService: {
notificationTitle: "Tracking Your Location",
notificationBody: "Location services active.",
},
});
Step 3: Required App Config (app.json)
{
"expo": {
"plugins": [
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow location access."
}
]
]
}
}
7. Background Tracking in Bare React Native
The easiest full-featured solution is:
react-native-background-geolocation
It provides:
-
Background mode
-
Geofencing
-
Headless tasks
-
Activity detection
-
Battery optimization
But in this tutorial, we will show the built-in OS background approach.
Android Setup
Add permission to AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Foreground service declaration:
<service
android:name=".LocationService"
android:foregroundServiceType="location" />
iOS Setup
Add to Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
iOS will show a blue indicator while tracking.
8. Best Practices for Background Tracking
✔ Inform users why location is needed
✔ Provide an in-app toggle to disable tracking
✔ Turn off tracking when not needed
✔ Avoid super-frequent updates—battery will drain fast
✔ Store location data efficiently (SQLite/local storage/server)
9. Combining Real-Time + Background Tracking
A robust implementation usually:
-
Listens to real-time foreground updates
-
Switches to background updates automatically
-
Stores location into a state manager or backend
-
Draws polyline route whenever app is opened
This is how apps like Gojek, Grab, Uber, and Strava operate.
Summary
You now know how to:
-
Track real-time position updates (foreground)
-
Build dynamic movement paths
-
Enable background location (Expo & Bare RN)
-
Configure iOS & Android permissions correctly
-
Handle accuracy + battery trade-offs
-
Build production-ready tracking systems
Your app is now capable of continuous movement tracking, both foreground and background.
Debugging & Common Gotchas
Working with maps and geolocation in React Native can sometimes be tricky due to native configurations, permissions, device limitations, and platform-specific behavior. This section covers the most common issues developers face—and how to fix them quickly.
Use this as your troubleshooting reference when something "just doesn't work."
1. The Map is Not Showing (Blank / Gray Screen)
This is the most common issue with react-native-maps.
Common Causes & Fixes
1. Google Maps API Key is Missing (Android)
If you see a blank map on Android:
✔ Check your AndroidManifest.xml:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY"/>
❗ Without this, Android renders a blank grid.
2. No iOS API Key When Using PROVIDER_GOOGLE
If using Google Maps on iOS:
Check AppDelegate.mm:
[GMSServices provideAPIKey:@"YOUR_API_KEY"];
Otherwise, the map may not load.
3. Simulator Doesn’t Support Maps Properly
Sometimes Android emulators lack Google Play Services.
✔ Create an emulator using an image labeled "Google Play Image"
✔ Avoid AOSP images—they do not support Maps
4. Region Settings Missing
If you don't pass initialRegion or region, the map may not center properly.
Add:
initialRegion={initialRegion}
5. Offline Device or No Internet
Google Maps requires the internet for initial tile load.
2. User Location Not Loading
Symptoms:
-
Blue dot does not appear
-
Location stays on “loading”
-
Permission denied errors
Fixes:
1. Missing iOS Permissions (Info.plist)
Add:
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show the map.</string>
2. Missing Android Permissions
Add to AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
For background tracking:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
3. Don't Forget to Request Runtime Permission (Android)
Bare RN needs:
await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
);
Expo requests permission automatically via:
Location.requestForegroundPermissionsAsync();
4. Simulator Has No Location
iOS Simulator:
✔ Debug → Location → Custom Location
✔ Or choose “City Bicycle Ride”
Android Emulator:
✔ Click "..." → "Location" → Set coordinates
3. Markers Not Appearing
Reasons:
1. Coordinates are undefined
Check that you log your coordinates:
console.log(latitude, longitude);
2. Marker is outside the visible region
Use fitToCoordinates():
mapRef.current.fitToCoordinates(points);
4. Polyline Not Showing
1. Coordinates array is empty
Verify length:
console.log(routeCoords.length);
2. Incorrect type
Ensure TypeScript uses:
LatLng[] = [{ latitude: number, longitude: number }]
3. Polyline out of map region
Adjust your region or call fitToCoordinates().
5. Google Directions API Not Working
Common errors:
1. REQUEST_DENIED
Your API key might be missing proper APIs:
✔ Enable these in Google Cloud Console:
-
Maps SDK for Android
-
Maps SDK for iOS
-
Directions API
2. Billing Not Enabled
Google Directions API requires billing, even for the free tier.
3. Invalid Polyline Error
Make sure you decode correctly:
polyline.decode(json.routes[0].overview_polyline.points);
6. Slow Map Performance
Tips:
✔ Use marker clustering
✔ Avoid rendering 100+ <Marker> elements directly
✔ Debounce calls to onRegionChange
✔ Use static map tiles where possible
✔ Ensure images for markers are optimized (small PNGs)
7. Location Accuracy Issues
1. Android Power Saving Mode
Some Android devices restrict background updates unless:
-
Battery optimization is disabled
-
App has location permission “Allow all the time”
2. iOS Simulator
iOS Simulator often has inaccurate or static GPS data.
3. enableHighAccuracy
Set:
enableHighAccuracy: true
But note this increases battery usage.
8. App Crashes on iOS After Adding Maps
Most common cause:
Missing CocoaPods Reinstall
After installation:
cd ios && pod install && cd ..
9. Location Updates Stop in Background
Fixes:
Expo:
✔ Must register a task via TaskManager.defineTask()
✔ Must enable background mode in app.json
Bare RN:
✔ Add UIBackgroundModes: location (iOS)
✔ Add foreground service (Android)
✔ Use high-quality libraries like react-native-background-geolocation
10. Android Foreground Service Crash
If using background tracking on Android:
Missing service in AndroidManifest.xml:
<service
android:name=".LocationService"
android:foregroundServiceType="location" />
Summary
In this section, you learned how to fix:
-
Blank maps
-
Missing API keys
-
Permission failures
-
Invisible markers
-
Polyline rendering problems
-
Poor tracking accuracy
-
Background location issues
-
Android/iOS platform quirks
With these fixes, you can debug nearly any issue in React Native Maps or geolocation.
Example App (Full TSX Code)
This section provides a complete, ready-to-run React Native Maps + Geolocation app written entirely in TypeScript (.tsx). It includes:
-
Permission handling
-
Fetching the user’s current location
-
Real-time movement tracking
-
A polyline showing the movement path
-
A marker for the current position
-
Universal compatibility (Expo & Bare RN)
You can drop this into an Expo project or adapt it for Bare React Native with only small permission changes.
1. Folder Structure (Recommended)
/src
/hooks
useUserLocation.ts
/components
MapTracker.tsx
App.tsx
This keeps the app modular and scalable.
Below is the complete code for each piece.
2. /src/hooks/useUserLocation.ts (TSX Custom Hook)
Works for Expo. For Bare RN, a variant is provided below.
import { useEffect, useState } from "react";
import * as Location from "expo-location";
import { Region, LatLng } from "react-native-maps";
interface UseUserLocation {
region: Region | null;
path: LatLng[];
}
export function useUserLocation(): UseUserLocation {
const [region, setRegion] = useState<Region | null>(null);
const [path, setPath] = useState<LatLng[]>([]);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") return;
const subscription = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
timeInterval: 2000,
distanceInterval: 5,
},
(location) => {
const { latitude, longitude } = location.coords;
const newPoint: LatLng = {
latitude,
longitude,
};
setPath((prev) => [...prev, newPoint]);
if (!region) {
setRegion({
latitude,
longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
}
}
);
return () => subscription.remove();
})();
}, []);
return { region, path };
}
3. /src/components/MapTracker.tsx
A reusable map component that displays:
-
Current user marker
-
Polyline movement path
-
Real-time location updates
import React from "react";
import { StyleSheet, View, ActivityIndicator } from "react-native";
import MapView, { Polyline, Marker } from "react-native-maps";
import { useUserLocation } from "../hooks/useUserLocation";
export default function MapTracker() {
const { region, path } = useUserLocation();
if (!region) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView
style={styles.map}
initialRegion={region}
showsUserLocation={true}
followsUserLocation={true}
>
{path.length > 1 && (
<Polyline
coordinates={path}
strokeWidth={4}
strokeColor="#e91e63"
/>
)}
{path.length > 0 && (
<Marker
coordinate={path[path.length - 1]}
title="Current Position"
/>
)}
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: "center", alignItems: "center" },
});
4. App.tsx — Main Entry Point (Expo)
import React from "react";
import { StatusBar } from "expo-status-bar";
import { SafeAreaView, StyleSheet } from "react-native";
import MapTracker from "./src/components/MapTracker";
export default function App() {
return (
<SafeAreaView style={styles.container}>
<StatusBar style="auto" />
<MapTracker />
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
});
5. Bare React Native Version (Hook Variant)
If you’re not using Expo, replace the hook with this version.
import { useEffect, useState } from "react";
import { PermissionsAndroid, Platform } from "react-native";
import Geolocation, { GeoPosition } from "react-native-geolocation-service";
import { Region, LatLng } from "react-native-maps";
export function useUserLocationBare(): {
region: Region | null;
path: LatLng[];
} {
const [region, setRegion] = useState<Region | null>(null);
const [path, setPath] = useState<LatLng[]>([]);
const requestPermission = async () => {
if (Platform.OS === "ios") return true;
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
};
useEffect(() => {
(async () => {
const hasPermission = await requestPermission();
if (!hasPermission) return;
const watchId = Geolocation.watchPosition(
(pos: GeoPosition) => {
const { latitude, longitude } = pos.coords;
const newPoint = { latitude, longitude };
setPath((prev) => [...prev, newPoint]);
if (!region) {
setRegion({
latitude,
longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
});
}
},
console.log,
{
enableHighAccuracy: true,
distanceFilter: 5,
interval: 2000,
}
);
return () => Geolocation.clearWatch(watchId);
})();
}, []);
return { region, path };
}
Everything else remains the same.
src/components/MapTracker.tsx
import React from 'react';
import { StyleSheet, View, ActivityIndicator } from 'react-native';
import MapView, { Polyline, Marker } from 'react-native-maps';
import { useUserLocationBare } from '../hooks/useUserLocation';
export default function MapTracker() {
const { region, path } = useUserLocationBare();
if (!region) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<MapView
style={styles.map}
initialRegion={region}
showsUserLocation={true}
followsUserLocation={true}
>
{path.length > 1 && (
<Polyline coordinates={path} strokeWidth={4} strokeColor="#e91e63" />
)}
{path.length > 0 && (
<Marker coordinate={path[path.length - 1]} title="Current Position" />
)}
</MapView>
);
}
const styles = StyleSheet.create({
map: { flex: 1 },
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
});
6. Example App Features
✔ Map initializes at the user’s current location
✔ Shows blue dot (native RN maps feature)
✔ Custom marker for the latest location
✔ Real-time tracking using watchPosition
✔ Path drawn as a polyline
✔ Works with both Expo and Bare React Native
✔ Modular and scalable folder structure
✔ 100% TypeScript
This example app forms the core foundation for any map or geolocation-based RN app, including:
-
Delivery tracking apps
-
Running/fitness path trackers
-
Location check-in apps
-
Taxi/ride-sharing apps
-
Logistics and fleet tracking
Conclusion
React Native makes it incredibly powerful and flexible to build location-aware mobile applications, and with just a few key libraries—react-native-maps, expo-location, or react-native-geolocation-service—you can implement everything from simple map displays to full real-time movement tracking.
In this tutorial, you learned how to:
-
Install and configure mapping and geolocation packages for both Expo and Bare React Native
-
Display your first map with
MapView -
Add markers, callouts, and custom icons
-
Implement marker clustering for large datasets
-
Draw routes with
Polyline, including real directions using Google’s Directions API -
Track real-time movement with
watchPosition -
Enable background location services for long-running tracking
-
Debug common issues such as blank maps, missing API keys, and permission problems
-
Build a complete TypeScript-based example app with clean, modular architecture
With these skills, you’re now fully equipped to build map-powered apps ranging from:
-
Delivery and ride-sharing platforms
-
Fitness tracking apps
-
Real-time navigation and routing tools
-
Location-based social features
-
Logistics and fleet monitoring systems
You can extend this project even further by adding features like geofencing, reverse geocoding, offline map tiles, or server-side route optimization.
Whether you're building a hobby project or a production-grade app, the patterns and examples in this guide provide a solid foundation for any React Native mapping experience.
You can find the full source code on our GitHub.
That's just the basics. If you need more deep learning about React Native, you can take the following cheap course:
- React Native - The Practical Guide [2025]
- The Complete React Native + Hooks Course
- The Best React Native Course 2025 (From Beginner To Expert)
- React Native: Mobile App Development (CLI) [2025]
- React - The Complete Guide 2025 (incl. Next.js, Redux)
- React Native Development Simplified [2025]
- React Native by Projects: From Basics to Pro [2025]
- Full Stack Ecommerce Mobile App: React Native & Node.js 2025
- Learning Management System Mobile App using React Native
- React Native: Advanced Concepts
Thanks!
