# Location sharing

Location sharing sends where a user is, either once or live for a while. This page covers the share dialog, how a live location keeps updating, and how a location renders in the message list.

## What users get

The user opens the attachment menu and picks Location. The Share Location dialog finds their position. They can share it as it is now, or turn on "Share live location for" and pick 15 minutes, 1 hour or 8 hours. Share sends the location as a message of its own. Attach adds it to the message they're writing instead; it shows above the input, and goes out with their text when they send.

In the message list, a location shows as a card that opens the position in a map. A static location reads "Current location". A live one reads "Live until" and the end time, and the user's own has a Stop sharing button. When the time is up or they stop, it reads "Location sharing ended". While their live share runs, their app keeps sending their position, so the card stays current for everyone in the channel. In the channel list, a channel whose latest message is a location shows "Location" as its preview.

![The Share Location dialog](https://getstream.io/docs-assets/images/91cc86cfbb0d.png)

![A static location card](https://getstream.io/docs-assets/images/02c0a109bd85.png)

![A live location card with Stop sharing](https://getstream.io/docs-assets/images/cfa75d102427.png)

![An ended live location card](https://getstream.io/docs-assets/images/a8fb4ae49823.png)

## How it works across the layers

- **State.** The composer's `locationComposer` holds the location being shared. A shared location is stored with its message as `message.shared_location`, not in `message.attachments`. `LiveLocationManager` keeps the user's live shares updated.
- **UI.** `AttachmentSelector` opens `ShareLocationDialog`. The composer shows an attached location above the input, and `Attachment` renders `message.shared_location` with `Geolocation`.
- **Layout.** Not involved.

### Sharing a location

`ShareLocationDialog` watches the browser's position with `navigator.geolocation` while it's open. Both buttons put that position into `messageComposer.locationComposer`, with the live duration when the user picked one:

- **Attach** leaves the location in the composer and closes the dialog. The next `MessageComposer.send()` puts it on the message.
- **Share** sends it at once with `MessageComposer.sendLocation()`, as a message of its own. When that fails, the dialog publishes an `api:location:share:failed` notification and stays open.

A location can't be shared in a thread: a thread's attachment menu has no Location entry, and `MessageComposer.sendLocation()` does nothing in a thread's composer. [Sharing a location](https://getstream.io/chat/docs/sdk/react/state/writing-messages/location/#sharing-a-location) explains the calls behind both buttons, and [Settings](https://getstream.io/chat/docs/sdk/react/state/writing-messages/location/#settings) sets the shortest duration a live share can have.

### Keeping a live location current

`LiveLocationManager` sends new positions for every live location the user is sharing. The React SDK creates one with `useLiveLocationSharingManager`:

1. On start, the manager loads the user's active live locations with `client.getUserLiveLocations()`.
2. When the user starts a new live share, the `live_location_sharing.started` event adds it. Stopping it or deleting its message removes it.
3. While at least one share is active, the manager calls your `watchLocation` function. Each position it reports is sent to every active share with `client.updateLiveLocation()`, at most once every `liveLocationManager.minUpdateThrottleMs` (3 seconds by default).
4. Each share is dropped when its end time passes. Stop sharing on the card calls `Channel.stopLiveLocationSharing()`.

The other clients see the new position on the message's `message.updated` event, and the card re-renders.

## Turning it on or off

Location sharing is on by default. Two sides decide:

- **The client:** `messageComposer.location.enabled`, `true` by default.
- **The server:** the channel type's `shared_locations` setting.

To turn it off in every composer:

```ts
client.config.set({
  messageComposer: { location: { enabled: false } },
});
```

The attachment menu offers Location only in channels, not in threads. [Configuration](https://getstream.io/chat/docs/sdk/react/state/state-and-entities/configuration/#who-decides-what) lists every setting both sides decide.

### Live locations need the manager

Without a `LiveLocationManager`, live shares are created, but their position never changes. Call `useLiveLocationSharingManager` once, in a component that stays mounted while the user is signed in, and pass it how to watch the position:

```tsx
import type { StreamChat, WatchLocation } from "stream-chat";
import { useLiveLocationSharingManager } from "stream-chat-react";

// Declared once: a new function on every render would create a new manager.
const watchLocation: WatchLocation = (handler) => {
  const watchId = navigator.geolocation.watchPosition((position) => {
    handler({
      latitude: position.coords.latitude,
      longitude: position.coords.longitude,
    });
  });

  return () => navigator.geolocation.clearWatch(watchId);
};

export const LiveLocationReporter = ({ client }: { client: StreamChat }) => {
  useLiveLocationSharingManager({ client, watchLocation });
  return null;
};
```

`watchLocation` receives a handler to call with each position, and returns a function that stops watching. To report less often, report on a timer instead, for example with `navigator.geolocation.getCurrentPosition()` every 30 seconds. The hook also takes `getDeviceId`, which sets the manager's `deviceId`; by default it's built from the device type, the browser and the user id, such as `web-desktop-chrome-user-1`. The device id stored on a new share comes from `messageComposer.location.getDeviceId` instead, a random id by default.

## Customizing

### Show a map in the dialog

The dialog has no map by default. Pass a `GeolocationMap` to it, and change the live durations with `shareDurations`, in milliseconds. This one uses [react-leaflet](https://react-leaflet.js.org/):

```tsx
import "leaflet/dist/leaflet.css";
import { MapContainer, Marker, TileLayer } from "react-leaflet";
import type {
  ShareGeolocationMapProps,
  ShareLocationDialogProps,
} from "stream-chat-react";
import { ShareLocationDialog } from "stream-chat-react";

const DialogMap = ({
  geolocationPositionError,
  latitude,
  loadingLocation,
  longitude,
  restartLocationWatching,
}: ShareGeolocationMapProps) => {
  if (loadingLocation) return <div>Finding your location…</div>;

  if (geolocationPositionError || latitude == null || longitude == null) {
    return (
      <div>
        <p>Your location isn't available.</p>
        <button onClick={restartLocationWatching} type="button">
          Try again
        </button>
      </div>
    );
  }

  return (
    <MapContainer
      center={[latitude, longitude]}
      style={{ height: 300, width: "100%" }}
      zoom={13}
    >
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker position={[latitude, longitude]} />
    </MapContainer>
  );
};

const shareDurations = [5 * 60 * 1000, 30 * 60 * 1000, 2 * 60 * 60 * 1000];

export const ShareLocationDialogWithMap = (props: ShareLocationDialogProps) => (
  <ShareLocationDialog
    {...props}
    GeolocationMap={DialogMap}
    shareDurations={shareDurations}
  />
);
```

Register it with `<WithComponents overrides={{ ShareLocationDialog: ShareLocationDialogWithMap }}>`. Durations shorter than one minute are left out. When the location isn't available, the example's **Try again** button calls `restartLocationWatching`. It stops the dialog's current position watch and starts a new one. [A map in the Share Location dialog](https://getstream.io/chat/docs/sdk/react/ui-cookbooks/custom-attachments/location-on-your-own-map/#a-map-in-the-share-location-dialog) explains every prop of `ShareGeolocationMapProps`.

![The Share Location dialog with a map and the live durations](https://getstream.io/docs-assets/images/ba35180e0386.png)

### Show a map in the message

`Geolocation` shows a placeholder that links to a map. Give it a `GeolocationMap`, which receives `latitude` and `longitude`, and pass it to `Attachment`:

```tsx
import { MapContainer, Marker, TileLayer } from "react-leaflet";
import type {
  AttachmentProps,
  GeolocationMapProps,
  GeolocationProps,
} from "stream-chat-react";
import { Attachment, Geolocation } from "stream-chat-react";

const MessageMap = ({ latitude, longitude }: GeolocationMapProps) => (
  <MapContainer
    center={[latitude, longitude]}
    style={{ height: 200, width: "100%" }}
    zoom={13}
  >
    <TileLayer
      attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
    />
    <Marker position={[latitude, longitude]} />
  </MapContainer>
);

const GeolocationWithMap = (props: GeolocationProps) => (
  <Geolocation {...props} GeolocationMap={MessageMap} />
);

export const AttachmentWithMaps = (props: AttachmentProps) => (
  <Attachment {...props} Geolocation={GeolocationWithMap} />
);
```

Register it as the `Attachment` override. A live card re-renders with each new position, so the map follows. `Geolocation` also takes `GeolocationAttachmentMapPlaceholder`, the component shown when there's no map.

The location preview above the input is the `location` item of the composer's preview set. To replace it, swap that item's `Component` in your own set, as [Custom previews](https://getstream.io/chat/docs/sdk/react/ui/components/message-composer-ui/#custom-previews) shows.

## Reference

- [Attachments](https://getstream.io/chat/docs/sdk/react/component-reference/message/attachments/): the `Attachment` props, including `Geolocation`.
- [Attachment Selector](https://getstream.io/chat/docs/sdk/react/component-reference/message-composer/attachment-selector/): the menu that opens the dialog.
- [Location hooks](https://getstream.io/chat/docs/sdk/react/reference/hooks/location-hooks/): `useLiveLocationSharingManager`.
- [MessageComposer API](https://getstream.io/chat/docs/sdk/react/reference/state/messagecomposer-api/): `locationComposer` and `sendLocation()`.
- [Configuration tree](https://getstream.io/chat/docs/sdk/react/reference/state/configuration-tree/): `messageComposer.location` and `liveLocationManager`.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/features/rich-content/location-sharing/](https://getstream.io/chat/docs/sdk/react/features/rich-content/location-sharing/).