React Native Maps and Location
Location and maps turn a generic app into a place-aware experience. React Native can read the device's GPS position and display an interactive map with custom markers. This topic covers getting the user's location and showing it on a map.
The Two Key Packages
Package Purpose ──────────────────────────────────────────────────────────── expo-location Read GPS coordinates, watch position react-native-maps Display an interactive map with markers
npx expo install expo-location npx expo install react-native-maps
Getting the User's Location
Location access requires permission. The device sends the app a latitude and longitude pair. Think of it as the phone saying "I am at this exact point on a globe."
import * as Location from 'expo-location';
import { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
export default function LocationScreen() {
const [location, setLocation] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
getLocation();
}, []);
async function getLocation() {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setError('Location permission denied.');
setLoading(false);
return;
}
const pos = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
setLocation(pos.coords);
setLoading(false);
}
if (loading) return <ActivityIndicator size="large" style={{ flex: 1 }} />;
if (error) return <Text>{error}</Text>;
return (
<View style={{ padding: 20 }}>
<Text>Latitude: {location.latitude.toFixed(6)}</Text>
<Text>Longitude: {location.longitude.toFixed(6)}</Text>
<Text>Accuracy: {Math.round(location.accuracy)} meters</Text>
</View>
);
}
Accuracy levels: Location.Accuracy.Lowest → coarse (city level, battery-friendly) Location.Accuracy.Balanced → neighbourhood level (default) Location.Accuracy.High → street level Location.Accuracy.Highest → GPS lock (most battery usage)
Displaying a Map
MapView from react-native-maps shows an interactive map. Give it a starting position with initialRegion.
import MapView, { Marker } from 'react-native-maps';
export default function MapScreen() {
const [location, setLocation] = useState(null);
useEffect(() => {
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') return;
const pos = await Location.getCurrentPositionAsync({});
setLocation(pos.coords);
})();
}, []);
if (!location) return <ActivityIndicator size="large" style={{ flex: 1 }} />;
return (
<MapView
style={{ flex: 1 }}
initialRegion={{
latitude: location.latitude,
longitude: location.longitude,
latitudeDelta: 0.01, // zoom level (smaller = more zoomed in)
longitudeDelta: 0.01,
}}
>
<Marker
coordinate={{ latitude: location.latitude, longitude: location.longitude }}
title="You are here"
description="Your current location"
/>
</MapView>
);
}
latitudeDelta / longitudeDelta — Zoom Guide: 0.001 → street level (one city block) 0.01 → neighbourhood level 0.1 → city level 1.0 → region/state level 10.0 → country level
Map with Multiple Markers
const restaurants = [
{ id: '1', name: 'The Curry House', lat: 28.6139, lng: 77.2090, rating: 4.5 },
{ id: '2', name: 'Pizza Palace', lat: 28.6150, lng: 77.2100, rating: 4.2 },
{ id: '3', name: 'Noodle Bar', lat: 28.6125, lng: 77.2080, rating: 4.7 },
];
<MapView style={{ flex: 1 }} initialRegion={{ latitude: 28.6139, longitude: 77.2090, latitudeDelta: 0.02, longitudeDelta: 0.02 }}>
{restaurants.map((r) => (
<Marker
key={r.id}
coordinate={{ latitude: r.lat, longitude: r.lng }}
title={r.name}
description={`Rating: ${r.rating} ⭐`}
/>
))}
</MapView>
Map View: ┌──────────────────────────────────────┐ │ │ │ 📍 The Curry House │ │ 📍 Pizza Palace │ │ 📍 Noodle Bar │ │ │ └──────────────────────────────────────┘ Each 📍 is a Marker component
Custom Marker Pins
<Marker coordinate={{ latitude: 28.6139, longitude: 77.2090 }}>
<View style={{
backgroundColor: '#007AFF',
padding: 8,
borderRadius: 20,
borderWidth: 2,
borderColor: 'white'
}}>
<Text style={{ color: 'white', fontSize: 12, fontWeight: 'bold' }}>⭐ 4.5</Text>
</View>
</Marker>
Watching Position in Real Time
Use watchPositionAsync to get continuous updates as the user moves. Useful for navigation, tracking, or fitness apps.
useEffect(() => {
let subscription;
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') return;
subscription = await Location.watchPositionAsync(
{ accuracy: Location.Accuracy.High, distanceInterval: 10 },
// distanceInterval: only fires when user moves at least 10 metres
(pos) => {
setLocation(pos.coords);
}
);
})();
return () => {
subscription?.remove(); // stop watching when screen leaves
};
}, []);
Reverse Geocoding — Coordinates to Address
Coordinates are numbers. Users want to see "123 Main Street, Mumbai". Reverse geocoding converts a latitude/longitude pair into a human-readable address.
async function getAddress(latitude, longitude) {
const [result] = await Location.reverseGeocodeAsync({ latitude, longitude });
return `${result.street}, ${result.city}, ${result.region}`;
// "MG Road, Bengaluru, Karnataka"
}
Forward Geocoding — Address to Coordinates
async function getCoordinates(address) {
const [result] = await Location.geocodeAsync(address);
return { latitude: result.latitude, longitude: result.longitude };
}
const coords = await getCoordinates('Taj Mahal, Agra');
// { latitude: 27.1751, longitude: 78.0421 }
Map Types
<MapView mapType="standard" ... /> // road map (default) <MapView mapType="satellite" ... /> // aerial photo <MapView mapType="hybrid" ... /> // satellite + road labels <MapView mapType="terrain" ... /> // topographic
Summary
Request foreground location permission before reading GPS. getCurrentPositionAsync gives a one-time position. watchPositionAsync gives continuous updates. MapView renders an interactive map and Marker places pins on it. Adjust latitudeDelta and longitudeDelta to control the zoom level. Use reverseGeocodeAsync to convert coordinates into readable addresses.
