DEV Community

Cathy Lai
Cathy Lai

Posted on

Adding Google Maps in React Native page

If you're building a property or travel app with React Native and want to show each listing on a map, this quick guide walks you through adding a simple Google Map — starting with a fixed location and ending with live coordinates from your Supabase database.


🧩 Step 1. Install the Map Library

Expo supports react-native-maps out of the box. Just install it:

npx expo install react-native-maps
Enter fullscreen mode Exit fullscreen mode

This gives you access to an interactive Google Map view and markers for iOS and Android.


🏠 Step 2. Create a Simple Fixed Map

Before connecting to data, start with something static.

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

export default function HouseDetails() {
  const region = {
    latitude: -36.8485, // Auckland
    longitude: 174.7633,
    latitudeDelta: 0.05,
    longitudeDelta: 0.05,
  };

  return (
    <View style={styles.container}>
      <MapView style={styles.map} initialRegion={region}>
        <Marker
          coordinate={{ latitude: -36.8485, longitude: 174.7633 }}
          title="Sample Home"
          description="123 Queen Street, Auckland"
        />
      </MapView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  map: { width: '100%', height: 300, borderRadius: 12 },
});
Enter fullscreen mode Exit fullscreen mode

That’s already enough to render a map centered on Auckland — no API key, no fuss.


⚙️ Step 3. Load Coordinates from Supabase

Let’s make it dynamic by reading each house’s latitude and longitude from your Supabase table.

import React, { useEffect, useMemo, useState } from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
import MapView, { Marker } from 'react-native-maps';
import { supabase } from '@/lib/supabase';

type House = {
  id: string;
  title: string | null;
  address: string | null;
  latitude: number | null;
  longitude: number | null;
};

export default function HouseDetails() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const [house, setHouse] = useState<House | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let mounted = true;
    (async () => {
      const { data, error } = await supabase
        .from('houses')
        .select('id, title, address, latitude, longitude')
        .eq('id', id)
        .single();

      if (mounted) {
        if (error) console.warn('Failed to load house:', error.message);
        setHouse(data ?? null);
        setLoading(false);
      }
    })();
    return () => { mounted = false; };
  }, [id]);

  const region = useMemo(() => {
    if (!house?.latitude || !house?.longitude) return null;
    return {
      latitude: house.latitude,
      longitude: house.longitude,
      latitudeDelta: 0.02,
      longitudeDelta: 0.02,
    };
  }, [house?.latitude, house?.longitude]);

  if (loading)
    return <View style={styles.center}><ActivityIndicator /></View>;

  if (!region)
    return <View style={styles.center} />;

  return (
    <View style={styles.container}>
      <MapView
        key={`${region.latitude},${region.longitude}`}
        style={styles.map}
        initialRegion={region}
        showsUserLocation={false}
        showsCompass={false}>
        <Marker
          coordinate={region}
          title={house?.title ?? 'Home'}
          description={house?.address ?? ''}
        />
      </MapView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  center: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  map: { width: '100%', height: 300, borderRadius: 12 },
});
Enter fullscreen mode Exit fullscreen mode

Why use useMemo()?

When coordinates come from asynchronous data, useMemo() ensures the region object stays stable between renders. Without it, the map might flicker or reset each time the component updates.


🌀 Without useMemo()

If your region object is recreated on every render (e.g., defined inline like this):

const region = {
  latitude: house.latitude,
  longitude: house.longitude,
  latitudeDelta: 0.02,
  longitudeDelta: 0.02,
};
Enter fullscreen mode Exit fullscreen mode

then React sees it as a new object each time — even if the coordinates haven’t changed.

So every time your component re-renders (for example, when: some unrelated state changes, data finishes loading, a parent component re-renders), the receives a new initialRegion prop and may reset its camera to the starting coordinates again.

That’s what looks like a “flicker” — the map seems to reload or jump back to the initial position.

** 🧠 With useMemo()

By memoizing the region:

const region = useMemo(() => ({
  latitude: house.latitude,
  longitude: house.longitude,
  latitudeDelta: 0.02,
  longitudeDelta: 0.02,
}), [house.latitude, house.longitude]);

Enter fullscreen mode Exit fullscreen mode

React will reuse the same object reference until the latitude or longitude actually changes — so the map stays still, no flicker.


✅ Wrap-Up

You now have a dynamic, interactive map inside your React Native app that reads real data from Supabase.

For small projects, this pattern is simple, fast, and deploy-ready — no Google Cloud setup required.

Top comments (0)