Location Sharing

Location sharing lets users share their current position or real-time location. Stream Chat supports static and live sharing.

There are two types of location sharing:

  • Static Location: A one-time location share that does not update over time.
  • Live Location: A real-time location share that updates over time for a specified duration.

Best Practices

  • Request location permissions just-in-time and explain why you need them.
  • Provide clear UI to stop live sharing and surface remaining time.
  • Avoid high-frequency updates; use reasonable intervals to save battery.
  • Handle permission denial gracefully by falling back to static sharing.
  • Store location metadata on the message to render without extra lookups.

The SDK handles location messages and updates, but your app must handle device location tracking.

Adding Live Location Sharing

This guide adds location sharing to a React Native chat app. The SDK provides the backend logic but not default UI. Use the examples in the Sample App or Expo Messaging app as a reference.

Setup

Install Dependencies

Install dependencies:

yarn add @react-native-community/geolocation
yarn add react-native-maps
  • @react-native-community/geolocation library is used to watch the current location of the user and then send it in the message
  • react-native-maps is used to display the location of the user using the native maps present on iOS and Android

Configure location permissions

To configure location permissions, you need to follow the platform-specific instructions below:

iOS

You need to include NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription in Info.plist to enable geolocation when using the app.

Android

To request access to location, you need to add the following line to your app's AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Android: Add API Key

On Android, one has to use Google Maps, which in turn requires you to obtain an API key for the Android SDK. On iOS, the native Apple Maps implementation is used and API keys are not necessary.

Add your API key to your manifest file (android/app/src/main/AndroidManifest.xml):

<application>
   <!-- You will only need to add this meta-data tag, but make sure it's a child of application -->
   <meta-data
     android:name="com.google.android.geo.API_KEY"
     android:value="Your Google maps API Key Here"/>
</application>

Implementation outline

You will build:

  • A modal for static/live sharing
  • A map renderer in MessageList
  • A full-screen map on tap
  • A way to stop live sharing and update the message

Creating a static/live location message

To create a static location message, set data on messageComposer.locationComposer, then call sendLocation:

const sendStaticLocation = async () => {
  const { latitude, longitude } = await getCurrentLocation();
  await messageComposer.locationComposer.setData({
    latitude,
    longitude,
  });
  await messageComposer.sendLocation();
};

To create a live location message, set data on locationComposer, then call sendLocation. It will watch location changes and update the message.

const sendLiveLocation = async () => {
  const { latitude, longitude } = await getCurrentLocation();
  await messageComposer.locationComposer.setData({
    durationMs: 600000, // 10 minutes
    latitude,
    longitude,
  });
  await messageComposer.sendLocation();
};

Preview of create location modal

An example implementation using the Geolocation watchPosition method to send the static or live location message can be found here:

To add the location sharing button, you can customize the InputButtons component in the Channel component. You can add a button that opens the modal to share the location.

import React, { useState } from "react";
import { Pressable, StyleSheet } from "react-native";
import { Channel, InputButtons as DefaultInputButtons } from "stream-chat-expo";
import { ShareLocationIcon } from "../icons/ShareLocationIcon";
import { LiveLocationCreateModal } from "./LocationSharing/CreateLocationModal";

const InputButtons: NonNullable<
  React.ComponentProps<typeof Channel>["InputButtons"]
> = (props) => {
  const [modalVisible, setModalVisible] = useState(false);

  const onRequestClose = () => {
    setModalVisible(false);
  };

  const onOpenModal = () => {
    setModalVisible(true);
  };

  return (
    <>
      <DefaultInputButtons {...props} hasCommands={false} />
      <Pressable style={styles.liveLocationButton} onPress={onOpenModal}>
        <ShareLocationIcon />
      </Pressable>
      <LiveLocationCreateModal
        visible={modalVisible}
        onRequestClose={onRequestClose}
      />
    </>
  );
};

const styles = StyleSheet.create({
  liveLocationButton: {
    paddingLeft: 5,
  },
});

export default InputButtons;

Showing the shared location in a message

If a message includes location data, you'll get the shared_location field with latitude/longitude and other details. The SDK does not provide default UI for this, so create a custom component to render the map and pass it via the MessageLocation prop on Channel.

To render the map, use react-native-maps and build a component that takes latitude/longitude.

Preview of message location

Example MessageLocation implementations:

Watching location globally with the manager

An app can have multiple active live locations across channels. Use client.getSharedLocations() to fetch active_live_locations for the current user.

To manage the live location sharing, we use the LiveLocationManager class from the stream-chat package. This manager allows you to watch the live location sharing for a specific message and update it in real-time.

The SDK exposes a context provider that supplies a LiveLocationManager instance and handles event registration/deregistration.

Wrap your app with LiveLocationManagerProvider from stream-chat-react-native. It expects a watchLocation function that watches device location.

Example watchLocation implementations:

import Geolocation from "@react-native-community/geolocation";

type LocationHandler = (value: { latitude: number; longitude: number }) => void;

export const watchLocation = (handler: LocationHandler) => {
  let watchId: number | null = null;
  watchId = Geolocation.watchPosition(
    (position) => {
      const { latitude, longitude } = position.coords;
      handler({ latitude, longitude });
    },
    (error) => {
      console.warn("Error watching location:", error);
    },
    {
      enableHighAccuracy: true,
      timeout: 20000,
      maximumAge: 1000,
      interval: 2000, // android only
    },
  );

  return () => {
    if (watchId) {
      Geolocation.clearWatch(watchId);
    }
  };
};

Use it in your app:

import { LiveLocationManagerProvider } from "stream-chat-react-native";

const App = () => {
  return (
    <LiveLocationManagerProvider watchLocation={watchLocation}>
      {/* Your app components */}
    </LiveLocationManagerProvider>
  );
};

Optionally pass getDeviceId (returns a string) to identify the device sharing the location.

To show a full-screen map, navigate to a detail screen when the user taps the location message. Use onPressMessage on Channel.

const App = () => {
  const onPressMessage: NonNullable<
    React.ComponentProps<typeof Channel>["onPressMessage"]
  > = (payload) => {
    const { message, defaultHandler, emitter } = payload;
    const { shared_location } = message ?? {};
    if (emitter === "messageContent" && shared_location) {
      navigation.navigate("MapScreen", shared_location);
    }
    defaultHandler?.();
  };

  return <Channel onPressMessage={onPressMessage}></Channel>;
};

Add the map details screen

This screen displays the location in a full-screen map, a stop-sharing button, and the end timestamp (if applicable).

Current LocationLive LocationLive Location Ended
Preview of chat screen
Preview of chat screen
Preview of chat screen

To keep the screen up to date, listen for backend events.

One of them being:

  • message.updated - Received when the message's shared_location changes.

Use useHandleLiveLocationEvents with messageId, channel, and onLocationUpdate.

onLocationUpdate is called when message.updated fires and can update the map marker.

Example MapScreen implementations:

You should now see the detailed map screen:

Stopping the live location sharing

To stop the live location sharing, you can use the stopLiveLocationSharing function from the client. It expects the current location response as argument.

const stopSharingLiveLocation = async () => {
  if (!locationResponse) {
    return;
  }
  await channel?.stopLiveLocationSharing(locationResponse);
};

Sample code

This cookbook is available as a full sample app: