DEV Community

Cover image for Stop Struggling with Maps in React Native — Here’s the Complete Guide
Arijit das
Arijit das

Posted on

Stop Struggling with Maps in React Native — Here’s the Complete Guide

☕ How I Actually Got Google Maps Working in React Native (and Why It Took Me Way Longer Than It Should Have)

So last week, I'm building this Coffee Shop Locator app. Simple idea, right? Show users nearby cafés on a map.

I figured I'd just drop in a <MapView> component, maybe add a few markers, and boom—done by lunch.

Yeah... that didn't happen. 😅

Three hours later, I'm staring at a blank gray screen, Googling "why is my react native map not showing" for the hundredth time, and questioning all my life choices.

But I finally got it working. And honestly? Once I understood what was actually happening under the hood, it all made sense.

So here's everything I learned—written for past me, and hopefully helpful for you too.


🧩 Why Maps Aren't Just "Install and Go"

Here's the thing nobody tells you upfront: React Native doesn't come with maps built-in.

When you think "map in my app," you're probably picturing something like Google Maps just... working. But that's not quite how it goes.

Under the hood, maps work completely differently on each platform:

  • Android uses the Google Maps SDK (native Java/Kotlin stuff)
  • iOS uses Apple's MapKit (native Swift code)

Your React Native app? That's all JavaScript. The map itself lives in native land.

So you need something that bridges the gap—something that lets you write simple JavaScript like this:

<MapView>
  <Marker coordinate={{ latitude: 28.61, longitude: 77.23 }} />
</MapView>
Enter fullscreen mode Exit fullscreen mode

...and then magically translates that into the actual native code that each platform understands.

That bridge is react-native-maps. And honestly, once I understood this, a lot of the setup headaches made more sense.


🌍 What react-native-maps Actually Does

Think of it like a translator at a conference.

You (JavaScript developer) speak one language. The native SDKs (Google Maps, Apple Maps) speak another. react-native-maps sits in the middle and makes sure everyone understands each other.

Without it, you'd be writing separate native modules in Kotlin and Swift yourself. No thanks. 😬


⚙️ The Part Where I Actually Set This Up (Android Edition)

Alright, this is where most tutorials lose me with vague instructions. So I'm gonna walk through exactly what I did, step by step.

🧭 Step 1: Get Your Google Maps API Key

Head over to the Google Cloud Console.

Click "Create API Key" and make sure you enable these three things:

  • ✅ Maps SDK for Android
  • ✅ Maps SDK for iOS
  • ✅ Places API (if you want search/autocomplete later)

Copy that API key. You'll need it in a second.


🔑 Step 2: Tell Android About Your API Key

Open up your Android manifest file:

android/app/src/main/AndroidManifest.xml
Enter fullscreen mode Exit fullscreen mode

Inside the <application> tag, paste this:

<meta-data
  android:name="com.google.android.geo.API_KEY"
  android:value="YOUR_GOOGLE_MAPS_API_KEY"/>
Enter fullscreen mode Exit fullscreen mode

This is basically how your Android app knows which API key to use when it talks to Google's servers.

⚠️ If you're using Expo and run into issues:
Sometimes Expo gets weird when you manually edit Android files. If your map still won't show up, or Expo throws errors during build, try adding the API key to your app.json instead:

"android": {
  "package": "com.example.coffeemap",
  "config": {
    "googleMaps": {
      "apiKey": "${GOOGLE_MAPS_API_KEY}"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

I ran into this myself—spent way too long trying to figure out why the manifest approach wasn't working. Turns out Expo sometimes overrides those manual changes during build. Using app.json lets Expo handle it properly.

📝 Pro tip: Don't leave your API key wide open. Go restrict it in the Cloud Console—tie it to your app's package name and SHA-1 fingerprint. (I'll show you how to get those in the next step.)


🔍 Step 3: Get Your SHA-1 Fingerprint

Okay, this part confused me at first. What even is a SHA-1 fingerprint?

Basically, it's a unique digital signature for your app. It comes from the keystore file that signs your APK. When you register your app with Google, they use this fingerprint to verify "yep, this request is really coming from YOUR app."

Each keystore = different SHA-1 = different identity.

Here's how to get it:

📂 Navigate to your Android folder

cd android
Enter fullscreen mode Exit fullscreen mode

💻 Run the signing report

On Windows (Command Prompt or PowerShell):

gradlew signingReport
Enter fullscreen mode Exit fullscreen mode

On macOS or Linux:

./gradlew signingReport
Enter fullscreen mode Exit fullscreen mode

You'll see a bunch of output. Look for something like this:

Variant: debug
SHA1: 12:34:56:AB:CD:EF:98:76:54:32:10:FF:EE:DD:CC:BB:AA:99:88
Package name: com.example.coffeemap
Enter fullscreen mode Exit fullscreen mode

Copy both the SHA-1 and your package name (also called applicationId).

Now go back to Google Cloud Console → Credentials → click on your API key → "Restrict Key" → Android Apps.

Add your package name and SHA-1 there.

⚠️ Important: Make sure you're using the right SHA-1 for your build type! There's one for debug (what you use when testing locally) and one for release (what you use for the Play Store). Use the debug one while you're developing.


🧹 Step 4: Clean Everything and Rebuild

Sometimes Android gets stubborn and caches old stuff. So do this:

cd android
./gradlew clean
cd ..
npx react-native run-android
Enter fullscreen mode Exit fullscreen mode

If the map is still blank (which happened to me), uninstall the app completely from your device/emulator and reinstall it. That finally did the trick for me.


🗺️ The Moment of Truth: A Working Map 🎉

Okay, after all that setup... here's the payoff. This is the simplest version that actually works:

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

export default function CoffeeMap() {
  return (
    <View style={styles.container}>
      <MapView
        provider="google"
        style={styles.map}
        initialRegion={{
          latitude: 28.6139, // New Delhi
          longitude: 77.2090,
          latitudeDelta: 0.05,
          longitudeDelta: 0.05,
        }}
        onMapReady={() => console.log("✅ Map is ready!")}
      >
        <Marker
          coordinate={{ latitude: 28.6139, longitude: 77.2090 }}
          title="Central Café"
          description="Your daily caffeine fix ☕"
        />
      </MapView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  map: { flex: 1 },
});
Enter fullscreen mode Exit fullscreen mode

When I finally saw that map load with my little red pin on it... honestly, such a satisfying moment. 😊


🧠 Understanding What These Components Actually Do

Once you have a working map, you'll probably want to do more with it. Here's a breakdown of the main components I used:

Component What It Does Example
<MapView> The actual map Base canvas for everything
<Marker> A pin on the map Mark café locations
<Callout> Info popup when you tap a marker Show details, ratings, hours
<Polyline> Draw lines on the map Show routes or paths
<Circle> Highlight a circular area Show delivery radius, etc.

Let me break down each one a bit more...


🗺️ <MapView> — Your Map Canvas

<MapView
  provider="google"
  style={{ flex: 1 }}
  initialRegion={{
    latitude: 37.7749,
    longitude: -122.4194,
    latitudeDelta: 0.05,
    longitudeDelta: 0.05,
  }}
  showsUserLocation={true}
  onMapReady={() => console.log("Map loaded!")}
/>
Enter fullscreen mode Exit fullscreen mode

Quick tip: The latitudeDelta and longitudeDelta control your zoom level. Smaller numbers = more zoomed in. I usually just play with these values until it looks right.


📍 <Marker> — The Pin

<Marker
  coordinate={{ latitude: 37.7749, longitude: -122.4194 }}
  title="Best Bites Restaurant"
  description="Open till 11 PM!"
  pinColor="#FF5733"
/>
Enter fullscreen mode Exit fullscreen mode

You can also use your own custom image instead of the default red pin:

image={require('../assets/restaurant-pin.png')}
Enter fullscreen mode Exit fullscreen mode

I did this for my coffee shops—added little coffee cup icons. Makes it feel more polished.


💬 <Callout> — Info Popup

<Marker coordinate={{ latitude: 37.7749, longitude: -122.4194 }}>
  <Callout>
    <View style={{ width: 200, padding: 10 }}>
      <Text style={{ fontWeight: "bold" }}>Best Bites</Text>
      <Text>⭐ 4.5 • Fast Delivery</Text>
    </View>
  </Callout>
</Marker>
Enter fullscreen mode Exit fullscreen mode

This is where you can get creative—show ratings, photos, business hours, whatever makes sense for your app.


🛣️ <Polyline> — Draw Routes

<Polyline
  coordinates={[
    { latitude: 37.7749, longitude: -122.4194 },
    { latitude: 37.7849, longitude: -122.4094 },
  ]}
  strokeColor="#007AFF"
  strokeWidth={4}
/>
Enter fullscreen mode Exit fullscreen mode

Perfect for showing delivery routes, walking paths, driving directions—anything that connects point A to point B.


🔵 <Circle> — Highlight Areas

<Circle
  center={{ latitude: 37.7749, longitude: -122.4194 }}
  radius={3000} // 3 km radius
  strokeColor="#FF0000"
  fillColor="rgba(255,0,0,0.2)"
/>
Enter fullscreen mode Exit fullscreen mode

I used this to show the delivery range for each café. Users could see at a glance if they're within range.


Perfect ✅ — here’s your updated, human-sounding, emotional, and storytelling-style section rewritten so it fits the Coffee Shop context (instead of the clinic locator).
Everything sounds natural and still feels developer-friendly 👇


🚀 Going Beyond the Basics: Making It Interactive

Here’s where it got fun for me. Just showing a static map felt... boring.
I wanted users to actually interact with it — move things around, see live updates, and feel like they’re really controlling where their coffee is coming from ☕.

So instead of a simple static café map, I built an interactive Coffee Shop Locator — where users can:

✅ Use their current GPS location

✅ Drag the coffee pin to pick their favorite café spot

✅ See live latitude and longitude updates

✅ Watch the map respond in real-time as they explore

Here’s the full code for that magic 👇

import React, { useEffect, useState } from "react";
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  ActivityIndicator,
} from "react-native";
import MapView, { Marker } from "react-native-maps";
import * as Location from "expo-location";
import Constants from "expo-constants";
import { scale, verticalScale } from "react-native-size-matters";

export default function CoffeeShopMapSelectionScreen() {
  const [region, setRegion] = useState({
    latitude: 20.5937,
    longitude: 78.9629,
    latitudeDelta: 0.05,
    longitudeDelta: 0.05,
  });

  const [loadingLocation, setLoadingLocation] = useState(false);
  const [hasPermission, setHasPermission] = useState(false);

  const googleApiKey = Constants.expoConfig?.extra?.GOOGLE_MAPS_API_KEY;
  console.log("Using API key:", googleApiKey);

  const requestLocationPermission = async () => {
    const { status } = await Location.requestForegroundPermissionsAsync();
    if (status !== "granted") {
      alert("Permission to access location was denied.");
      return;
    }
    setHasPermission(true);
  };

  const fetchCurrentLocation = async () => {
    try {
      setLoadingLocation(true);
      const { coords } = await Location.getCurrentPositionAsync({});
      setRegion((prev) => ({
        ...prev,
        latitude: coords.latitude,
        longitude: coords.longitude,
      }));
    } catch (error) {
      console.log("Error fetching current location:", error);
    } finally {
      setLoadingLocation(false);
    }
  };

  useEffect(() => {
    requestLocationPermission();
  }, []);

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.headerTitle}>☕ Pick Your Coffee Spot</Text>
        <TouchableOpacity
          onPress={fetchCurrentLocation}
          style={styles.currentLocationBtn}
        >
          {loadingLocation ? (
            <ActivityIndicator color="#fff" />
          ) : (
            <Text style={styles.currentLocationText}>Use Current Location</Text>
          )}
        </TouchableOpacity>
      </View>

      <MapView
        style={styles.map}
        provider="google"
        initialRegion={region}
        onRegionChangeComplete={setRegion}
        showsUserLocation={true}
        onMapReady={() => console.log("✅ Map ready")}
      >
        <Marker
          coordinate={{
            latitude: region.latitude,
            longitude: region.longitude,
          }}
          draggable
          onDragEnd={(e) => {
            const { latitude, longitude } = e.nativeEvent.coordinate;
            setRegion({ ...region, latitude, longitude });
          }}
          pinColor="#b76e79"
          title="Your Coffee Spot"
          description="Drag to adjust location"
        />
      </MapView>

      <View style={styles.locationDetails}>
        <Text style={styles.coordText}>
          🌍 Latitude: {region.latitude.toFixed(6)}
        </Text>
        <Text style={styles.coordText}>
          📍 Longitude: {region.longitude.toFixed(6)}
        </Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: "#fff" },
  map: { flex: 1 },
  header: {
    paddingHorizontal: scale(16),
    paddingVertical: verticalScale(8),
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
  },
  headerTitle: {
    fontSize: 16,
    fontWeight: "600",
    color: "#222",
  },
  currentLocationBtn: {
    backgroundColor: "#8B4513",
    paddingHorizontal: scale(12),
    paddingVertical: verticalScale(6),
    borderRadius: 10,
  },
  currentLocationText: {
    color: "#fff",
    fontSize: 14,
    fontWeight: "500",
  },
  locationDetails: {
    backgroundColor: "#F9F9F9",
    paddingVertical: verticalScale(10),
    paddingHorizontal: scale(16),
    borderTopWidth: 1,
    borderColor: "#EAEAEA",
  },
  coordText: {
    fontSize: 14,
    color: "#333",
    marginBottom: 4,
  },
});
Enter fullscreen mode Exit fullscreen mode

💡 What’s Happening Behind the Scenes

  1. Permission Request: The app politely asks for location access using expo-location.
  2. GPS Centering: One tap centers the map right on your current location.
  3. Draggable Pin: Users can drag the pin to choose exactly where their favorite café is.
  4. Live Updates: Latitude and longitude update in real time below the map.

Now, it doesn’t just show a map — it feels alive ☕✨

🎨 Making Your Map Look Less... Generic

One thing I realized pretty quickly—the default Google Maps style is fine, but it doesn't really match every app's vibe.

You can completely customize how your map looks using JSON style files. Dark mode, minimalist, retro, whatever fits your design.

Create a file called map-style.json:

[
  {
    "featureType": "all",
    "elementType": "labels",
    "stylers": [{ "visibility": "off" }]
  }
]
Enter fullscreen mode Exit fullscreen mode

Then apply it:

import mapStyle from '../assets/map-style.json';

<MapView
  customMapStyle={mapStyle}
  style={{ flex: 1 }}
/>
Enter fullscreen mode Exit fullscreen mode

Pro tip: Use the Google Map Styling Wizard to visually design your style. Way easier than hand-writing JSON. You just click around, pick colors, toggle elements on and off, then download the JSON file when you're done.

I made mine slightly darker with muted colors so it didn't compete with my UI elements. Made a huge difference in how professional the app felt.


Final Thoughts

Look, I'm not gonna lie—getting maps working in React Native was more work than I expected. But once I understood the why behind each step, it all clicked.

If you're stuck on a blank gray screen right now, trust me—you're probably just one small config fix away from getting it working. Double-check your API key, your SHA-1, your manifest. Clean and rebuild. Uninstall and reinstall if you have to.

And when it finally loads? When you see that map render with your markers and your custom styling? Honestly, it's worth the hassle.

Good luck. You got this. ☕


Questions? Hit me up in the comments. I'll try to help if I can.

Top comments (0)