yarn add @react-native-community/geolocation
yarn add react-native-mapsLocation 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:
@react-native-community/geolocationlibrary is used to watch the current location of the user and then send it in the messagereact-native-mapsis used to display the location of the user using the native maps present on iOS and Android
yarn add expo-location
yarn add react-native-mapsexpo-locationlibrary is used to watch the current location of the user and then send it in the messagereact-native-mapsis 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>Configure the location permissions in your app.json or app.config.js file:
{
"expo": {
"plugins": [
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
}
]
]
}
}Alternatively, you can follow this guide.
Android: Add API Key
To set up the google maps API key, you can add the following to your app.json or app.config.js file:
{
"expo": {
"android": {
"config": {
"googleMaps": {
"apiKey": "Your Google maps API Key Here"
}
}
}
}
}Alternatively, you can follow this guide.
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();
};
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.

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);
}
};
};import * as Location from "expo-location";
export type LocationHandler = (value: {
latitude: number;
longitude: number;
}) => void;
export const watchLocation = (handler: LocationHandler) => {
let subscription: Location.LocationSubscription | null = null;
Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
distanceInterval: 0,
// Android only: these option are ignored on iOS
timeInterval: 2000,
},
(location) => {
const { latitude, longitude } = location.coords;
handler({ latitude, longitude });
},
(error) => {
console.warn("Error watching location:", error);
},
).then((sub) => {
subscription = sub;
});
return () => {
subscription?.remove();
};
};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.
Navigating from the message to the map detail screen
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>;
};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) {
// Create url params from shared_location
const params = Object.entries(shared_location)
.map(([key, value]) => `${key}=${value}`)
.join("&");
router.push(`/map/${message.id}?${params}`);
}
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 Location | Live Location | Live Location Ended |
|---|---|---|
![]() | ![]() | ![]() |
To keep the screen up to date, listen for backend events.
One of them being:
message.updated- Received when the message'sshared_locationchanges.
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:


