# Location Sharing

Location sharing allows users to send a static position or share their real-time location with other participants in a channel. Stream Chat supports both static and live location 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 sharing that updates over time.

<admonition type="info">

The SDK handles location message creation and updates, but location tracking must be implemented by the application using device location services.

</admonition>

## Enabling location sharing

The location sharing feature must be activated at the channel level before it can be used. You have two configuration options: activate it for a single channel using configuration overrides, or enable it globally for all channels of a particular type via [channel type settings](/chat/docs/node/channel_features/).

<Tabs>

```javascript label="JavaScript"
// Enabling it for a channel
await channel.updatePartial({
  set: {
    config_overrides: {
      shared_locations: true,
    },
  },
});

// Enabling it for a channel type
const update = await client.updateChannelType("messaging", {
  shared_locations: true,
});
```

```python label="Python"
# Enabling it for a channel type
client.chat.update_channel_type(
    "messaging",
    automod="disabled",
    automod_behavior="flag",
    max_message_length=5000,
    shared_locations=True,
)
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Enabling it for a channel type
client.chat.update_channel_type('messaging', Models::UpdateChannelTypeRequest.new(
  shared_locations: true
))
```

```php label="PHP"
// Enabling it for a channel type
$client->updateChannelType("messaging", new Models\UpdateChannelTypeRequest(sharedLocations: true));
```

```java label="Java"
// Enabling it for a channel type
chat.updateChannelType("messaging", UpdateChannelTypeRequest.builder()
    .automod("disabled")
    .automodBehavior("flag")
    .maxMessageLength(5000)
    .sharedLocations(true)
    .build()).execute();
```

```csharp label="C#"
// Enabling it for a channel type
await chat.UpdateChannelTypeAsync("messaging", new UpdateChannelTypeRequest
{
    SharedLocations = true,
});
```

```go label="Go"
// Enabling it for a channel type
client.Chat().UpdateChannelType(ctx, "messaging", &getstream.UpdateChannelTypeRequest{
	Automod:          "disabled",
	AutomodBehavior:  "flag",
	MaxMessageLength: 5000,
	SharedLocations:  getstream.PtrTo(true),
})
```

</Tabs>

## Sending static location

Static location sharing allows you to send a message containing a static location.

<Tabs>

```swift label="Swift"
/// Get a channel controller for the channel.
let channelController = chatClient.channelController(for: ChannelId(type: .messaging, id: "general"))

/// Send a message with a static location.
let location = LocationInfo(latitude: 10, longitude: 10)
channelController.sendStaticLocation(location)

/// Alternatively, you can create a new message with a `NewLocationInfo` object and endAt as nil.
let newLocation = NewLocationInfo(latitude: 10, longitude: 10, endAt: nil)
channelController.createNewMessage(text: "", location: newLocation)
```

```javascript label="JavaScript"
const channel = client.channel("type", "id");

// Send a message with a static location.
channel.sendSharedLocation({
  created_by_device_id: "device-id",
  latitude: 10,
  longitude: 10,
  message_id: "message-id",
});
```

```kotlin label="Kotlin"
// Send a static location message.
chatClient.sendStaticLocation(
    cid = "channelType:channelId",
    latitude = -8.0421,
    longitude = -34.9351,
    deviceId = "my-device-id",
).enqueue { /* ... */ }
```

```go label="Go"
channel := client.Chat().Channel("messaging", channelID)

// Send a message with a SharedLocation object
channel.SendMessage(ctx, &getstream.SendMessageRequest{
	Message: getstream.MessageRequest{
		UserID: getstream.PtrTo(userID),
		SharedLocation: &getstream.SharedLocation{
			Longitude:         longitude,
			Latitude:          latitude,
			CreatedByDeviceID: getstream.PtrTo("test-device"),
		},
	},
})
```

```java label="Java"
// Create shared location
SharedLocation sharedLocation = SharedLocation.builder()
    .createdByDeviceID(deviceId)
    .latitude(latitude)
    .longitude(longitude)
    .build();

// Send message with shared location
MessageRequest messageRequest = MessageRequest.builder()
    .sharedLocation(sharedLocation)
    .userID(userId)
    .build();

var response = chat.channel(channelType, channelId)
    .sendMessage(SendMessageRequest.builder()
        .message(messageRequest)
        .build());
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Send a message with a static location
channel_id = SecureRandom.uuid
response = client.chat.send_message('messaging', channel_id, Models::SendMessageRequest.new(
  message: Models::MessageRequest.new(
    user_id: user_id,
    shared_location: Models::SharedLocation.new(
      created_by_device_id: 'test-device',
      latitude: 40.7128,
      longitude: -74.0060
    )
  )
))
```

```csharp label="C#"
// Create a shared location for the initial message
var location = new SharedLocation
{
    Longitude = longitude,
    Latitude = latitude,
    EndAt = null, // null for static location
    CreatedByDeviceID = "test-device",
};

// Send a message with shared location
await chat.SendMessageAsync("channel-type", "channel-id", new SendMessageRequest
{
    Message = new MessageRequest
    {
        Text = "Test message for shared location",
        SharedLocation = location,
    },
});
```

```python label="Python"
from getstream.models import MessageRequest, SharedLocation

# Send a static location message
channel = client.chat.channel("messaging", "channel-id")

channel.send_message(
    MessageRequest(
        text="Message with static location",
        shared_location=SharedLocation(
            latitude=37.7749,
            longitude=-122.4194,
            created_by_device_id="test_device_id",
            # No 'end_at' for static location
        ),
        user_id=user_id,
    )
)
```

```php label="PHP"
// Send a static location message
$client->sendMessage("messaging", "general", new Models\SendMessageRequest(
    message: new Models\MessageRequest(
        text: "Message with static location",
        userID: $userId,
        sharedLocation: new Models\SharedLocation(
            createdByDeviceID: "test_device_id",
            latitude: 37,
            longitude: -122,
        ),
    ),
));
```

```dart label="Dart"
final locationCoordinates = LocationCoordinates(
  latitude: 37.7749,
  longitude: -122.4194,
);

// Send a static location message.
final response = await channel.sendStaticLocation(
  location: locationCoordinates,
  createdByDeviceId: 'test_device_id', // Optional
);
```

</Tabs>

## Starting live location sharing

Live location sharing enables real-time location updates for a specified duration. The SDK manages the location message lifecycle, but your application is responsible for providing location updates.

<Tabs>

```swift label="Swift"
/// Get a channel controller for the channel.
let channelController = chatClient.channelController(for: ChannelId(type: .messaging, id: "general"))

/// Send a message with a live location with the initial coordinates.
let location = LocationInfo(latitude: 10, longitude: 10)
let oneMinuteFromNow = Date().addingTimeInterval(60)
channelController.startLiveLocationSharing(location, endDate: oneMinuteFromNow)
```

```javascript label="JavaScript"
const channel = client.channel("type", "id");

// Send a message with a live location.
// Live location differs from the static location by the termination timestamp end_at.
channel.sendSharedLocation({
  created_by_device_id: "device-id",
  end_at: "2225-07-22T09:30:12.507Z",
  latitude: 10,
  longitude: 10,
  message_id: "message-id",
});
```

```kotlin label="Kotlin"
// Start a live location sharing and automatically stops after 10 minutes.
val tenMinutesFromNow = Date().apply { time += 10.minutes.inWholeMilliseconds }
chatClient.startLiveLocationSharing(
    cid = "channelType:channelId",
    latitude = -8.0421,
    longitude = -34.9351,
    deviceId = "my-device-id",
    endAt = tenMinutesFromNow,
).enqueue { /* ... */ }
```

```go label="Go"
channel := client.Chat().Channel("messaging", channelID)

// Send a message with a SharedLocation object with end_at for live location
t := time.Now().Add(1 * time.Hour)
endAt := getstream.Timestamp{Time: &t}
channel.SendMessage(ctx, &getstream.SendMessageRequest{
	Message: getstream.MessageRequest{
		UserID: getstream.PtrTo(userID),
		SharedLocation: &getstream.SharedLocation{
			Longitude:         longitude,
			Latitude:          latitude,
			EndAt:             &endAt,
			CreatedByDeviceID: getstream.PtrTo("test-device"),
		},
	},
})
```

```java label="Java"
// Create shared location with end_at for live location
SharedLocation sharedLocation = SharedLocation.builder()
    .createdByDeviceID(deviceId)
    .latitude(latitude)
    .longitude(longitude)
    .endAt(endAt)
    .build();

// Send message with shared location
MessageRequest messageRequest = MessageRequest.builder()
    .sharedLocation(sharedLocation)
    .userID(userId)
    .build();

var response = chat.channel(channelType, channelId)
    .sendMessage(SendMessageRequest.builder()
        .message(messageRequest)
        .build());
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Send a message with a live location (with end_at)
channel_id = SecureRandom.uuid
response = client.chat.send_message('messaging', channel_id, Models::SendMessageRequest.new(
  message: Models::MessageRequest.new(
    user_id: user_id,
    shared_location: Models::SharedLocation.new(
      created_by_device_id: SecureRandom.uuid,
      latitude: 40.7128,
      longitude: -74.0060,
      end_at: (Time.now + 3600).iso8601
    )
  )
))
```

```csharp label="C#"
// Create a shared location for live location sharing (with EndAt)
var location = new SharedLocation
{
    Longitude = longitude,
    Latitude = latitude,
    EndAt = DateTime.UtcNow.AddHours(1), // Set duration for live location
    CreatedByDeviceID = "test-device",
};

// Send a message with shared location
await chat.SendMessageAsync("channel-type", "channel-id", new SendMessageRequest
{
    Message = new MessageRequest
    {
        Text = "Test message for live location sharing",
        SharedLocation = location,
    },
});
```

```python label="Python"
import datetime
from getstream.models import MessageRequest, SharedLocation

# Send a live location message (with end_at)
now = datetime.datetime.now(datetime.timezone.utc)
one_hour_later = now + datetime.timedelta(hours=1)

channel = client.chat.channel("messaging", "channel-id")

channel.send_message(
    MessageRequest(
        text="Message with live location",
        shared_location=SharedLocation(
            latitude=37.7749,
            longitude=-122.4194,
            created_by_device_id="test_device_id",
            end_at=one_hour_later,
        ),
        user_id=user_id,
    )
)
```

```php label="PHP"
// Send a live location message (with end_at)
$client->sendMessage("messaging", "general", new Models\SendMessageRequest(
    message: new Models\MessageRequest(
        userID: $userId,
        sharedLocation: new Models\SharedLocation(
            createdByDeviceID: "test_device_id",
            latitude: 37,
            longitude: -122,
            endAt: new \DateTime("+1 hour"),
        ),
    ),
));
```

```dart label="Dart"
final initialLocation = LocationCoordinates(
  latitude: 37.7749,
  longitude: -122.4194,
);

// Send a live location message with an end time.
final response = await channel.startLiveLocationSharing(
  location: initialLocation,
  endSharingAt: DateTime.timestamp().add(Duration(hours: 1)), // 1 hour later
  createdByDeviceId: 'test_device_id', // Optional
);
```

</Tabs>

## Stopping live location sharing

You can stop live location sharing for a specific message using the message controller:

<Tabs>

```swift label="Swift"
/// Get the message controller for the live location message
let messageController = chatClient.messageController(
    cid: channelId,
    messageId: liveLocationMessageId
)

/// Stop live location sharing
messageController.stopLiveLocationSharing()
```

```javascript label="JavaScript"
const channel = client.channel("type", "id");

// to stop location sharing at least message id has to be provided
channel.stopLiveLocationSharing({
  message_id: "message-id",
});
```

```kotlin label="Kotlin"
// Stop a live location sharing.
chatClient.stopLiveLocationSharing(
    messageId = "live-location-message-id",
    deviceId = "my-device-id",
).enqueue { /* ... */ }
```

```go label="Go"
// Stop live location sharing by setting EndAt to now
t := time.Now()
endAt := getstream.Timestamp{Time: &t}
_, err := client.UpdateLiveLocation(ctx, &getstream.UpdateLiveLocationRequest{
	UserID:    getstream.PtrTo(userID),
	MessageID: messageID,
	Latitude:  &longitude,
	Longitude: &latitude,
	EndAt:     &endAt,
})
```

```java label="Java"
// Stop live location sharing by setting endAt to now
client.updateLiveLocation(UpdateLiveLocationRequest.builder()
    .messageID(initialMessage.getId())
    .UserID(userId)
    .latitude(latitude)
    .longitude(longitude)
    .endAt(new Date())
    .build())
    .execute();
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Stop live location sharing by setting end_at to now
client.common.update_live_location(Models::UpdateLiveLocationRequest.new(
  message_id: message_id,
  latitude: 40.7128,
  longitude: -74.0060,
  end_at: Time.now.iso8601
), user_id)
```

```csharp label="C#"
// Stop live location sharing by setting EndAt to now
await client.UpdateLiveLocationAsync(new UpdateLiveLocationRequest
{
    MessageID = liveLocationMessageId, // The ID of the live location message
    Longitude = longitude,
    Latitude = latitude,
    EndAt = DateTime.UtcNow, // Set EndAt to now to stop sharing
});
```

```python label="Python"
import datetime

# Stop live location sharing by setting end_at to now
client.update_live_location(
    message_id=message_id,
    end_at=datetime.datetime.now(datetime.timezone.utc),
    latitude=new_latitude,
    longitude=new_longitude,
    user_id=user_id,
)
```

```php label="PHP"
// Stop live location sharing by setting endAt to now
$client->updateLiveLocation($userId, new Models\UpdateLiveLocationRequest(
    messageID: $messageId,
    latitude: $latitude,
    longitude: $longitude,
    endAt: new \DateTime("now"),
));
```

```dart label="Dart"
// Stop live location sharing for a specific message.
final location = await client.stopLiveLocation(
  messageId: liveLocationMessageId,
  createdByDeviceId: 'test_device_id', // Optional
);
```

</Tabs>

## Updating live location

Your application must implement location tracking and provide updates to the SDK. The SDK handles updating all the current user's active live location messages and provides a throttling mechanism to prevent excessive API calls.

<Tabs>

```swift label="Swift"
/// Get the current user controller.
let currentUserController = chatClient.currentUserController()

/// Set delegate to receive live location updates.
currentUserController.delegate = self

/// Load active live locations (call only once at app start or when chat is initialized)
currentUserController.loadActiveLiveLocationMessages()

/// Update location when device location changes.
func locationDidUpdate(_ newLocation: LocationInfo) {
    currentUserController.updateLiveLocation(newLocation)
}
```

```javascript label="JavaScript"
// simple call to update a location
await client.updateLocation({
  latitude: 1,
  longitude: 2,
  message_id: "message-id",
});
```

```kotlin label="Kotlin"
// Query user's active live locations (call only once when user is connected).
chatClient.queryActiveLocations().enqueue { /* ... */ }

// Listen for changes in current user's active live locations.
chatClient.globalStateFlow
    .flatMapLatest { it.currentUserActiveLiveLocations }
    .onEach { userActiveLiveLocations ->
        if (userActiveLiveLocations.isEmpty()) {
            // Stop receiving device location updates if there's no active live locations.
        } else {
            // Start receiving device location updates (check for location permissions).
        }
    }
    .launchIn(coroutineScope)

// Update live location when device location changes.
for (deviceLocation in result.locations) {
    userActiveLiveLocations
        .filterNot { it.endAt?.before(Date()) ?: false } // Filter out expired locations
        .forEach { activeLiveLocation ->
            chatClient.updateLiveLocation(
                messageId = activeLiveLocation.messageId,
                latitude = deviceLocation.latitude,
                longitude = deviceLocation.longitude,
                deviceId = "my-device-id",
            ).enqueue { /* ... */ }
        }
}
```

```go label="Go"
// Get current user active live locations
resp, err := client.GetUserLiveLocations(ctx, &getstream.GetUserLiveLocationsRequest{
	UserID: getstream.PtrTo(userID),
})

// Update active live locations of the current user
for _, location := range resp.Data.ActiveLiveLocations {
	_, err = client.UpdateLiveLocation(ctx, &getstream.UpdateLiveLocationRequest{
		UserID:    getstream.PtrTo(userID),
		MessageID: location.MessageID,
		Latitude:  &newLatitude,
		Longitude: &newLongitude,
	})
}
```

```java label="Java"
// Get active live locations
var activeLocations = client.getUserLiveLocations(GetUserLiveLocationsRequest.builder()
    .UserID(userId)
    .build())
    .execute().getData();

// Update active live locations
for (var location : activeLocations.getActiveLiveLocations()) {
    client.updateLiveLocation(UpdateLiveLocationRequest.builder()
        .messageID(location.getMessageID())
        .UserID(userId)
        .latitude(newLatitude)
        .longitude(newLongitude)
        .build())
        .execute();
}
```

```ruby label="Ruby"
require 'getstream_ruby'
Models = GetStream::Generated::Models

# Get the user's active live locations
response = client.common.get_user_live_locations(user_id)

# Update the user's live location
client.common.update_live_location(Models::UpdateLiveLocationRequest.new(
  message_id: message_id,
  latitude: new_latitude,
  longitude: new_longitude
), user_id)
```

```csharp label="C#"
// Get all active live locations for the current user
var activeLocations = await client.GetUserLiveLocationsAsync(userID);

// Update all active live locations for the user
foreach (var location in activeLocations.ActiveLiveLocations)
{
    await client.UpdateLiveLocationAsync(new UpdateLiveLocationRequest
    {
        MessageID = location.MessageID,
        Longitude = newLongitude, // updated longitude
        Latitude = newLatitude,   // updated latitude
        EndAt = endAt,            // keep the same end time
    });
}
```

```python label="Python"
# Update the user's live location (e.g., when device location changes)
client.update_live_location(
    message_id=message_id,
    latitude=new_latitude,
    longitude=new_longitude,
    user_id=user_id,
)
```

```php label="PHP"
// Update the user's live location (e.g., when device location changes)
$client->updateLiveLocation($userId, new Models\UpdateLiveLocationRequest(
    messageID: $messageId,
    latitude: $newLatitude,
    longitude: $newLongitude,
));
```

```dart label="Dart"
// Update the user's live location (e.g., when device location changes)
final updatedLocation = await client.updateLiveLocation(
  messageId: liveLocationMessageId,
  createdByDeviceId: 'test_device_id', // Optional
  location: LocationCoordinates(
    latitude: newLatitude,
    longitude: newLongitude,
  ),
);
```

</Tabs>

Whenever the location is updated, the message will automatically be updated with the new location.

The SDK will also notify your application when it should start or stop location tracking as well as when the active live location messages change.

<Tabs>

```swift label="Swift"
extension SomeObject: CurrentChatUserControllerDelegate {

    /// Called when the user starts sharing a live location and it wasn't already sharing a live location
    func currentUserControllerDidStartSharingLiveLocation(
        _ controller: CurrentChatUserController
    ) {
        // Start location monitoring (Needs to be implemented by the application)
        startLocationTracking()
    }

    /// Called when all live location sharing stops
    func currentUserControllerDidStopSharingLiveLocation(
        _ controller: CurrentChatUserController
    ) {
        // Stop location monitoring
        stopLocationTracking()
    }

    /// Called when active live location messages change
    func currentUserController(
        _ controller: CurrentChatUserController,
        didChangeActiveLiveLocationMessages messages: [ChatMessage]
    ) {
        // You can use this delegate method to track the active live location messages and
        // if needed render all of them in a UI component or you can use this for finer control
        // when to start or stop tracking locations.
    }

    /// Called when a live location update fails
    func currentUserController(
        _ controller: CurrentChatUserController,
        didFailToUpdateLiveLocation location: SharedLocation,
        with error: Error
    ) {
        // Handle error if needed
    }

}
```

```dart label="Dart"
// You can listen to the current user's active live locations in order to determine when to start
// or stop location tracking.
_activeLiveLocationsSubscription = _client.state.activeLiveLocationsStream
    .distinct((prev, curr) => prev.length == curr.length)
    .listen((locations) async {
  // If there are no more active locations to update, stop tracking.
  if (locations.isEmpty) return _stopTrackingLocation();
  // Otherwise, start tracking the user's location.
  return _startTrackingLocation();
});
```

```javascript label="JavaScript"
// reporter function necessary for LiveLocationManager
const watchLocation = (handler) => {
  const timer = setInterval(() => {
    // retrieval of current location is a app-specific logic
    getCurrentPosition((position) => {
      handler({
        latitude: position.coords.latitude,
        longitude: position.coords.longitude,
      });
    });
  }, 5000);

  return () => {
    clearInterval(timer);
  };
};

// the manager takes care of registering/ unregistering and reporting location updates
const manager = new LiveLocationManager({
  client,
  getDeviceId, // function should generate a unique device id
  watchLocation, // function should retrieve the location and pass it to the handler function
});

// to start watching and reporting the manager subscriptions have to be initiated
manager.init();

// to stop watching and reporting the manager subscriptions have cleaned up
manager.unregisterSubscriptions();
```

</Tabs>

## Events

Whenever a location is created or updated, the following WebSocket events will be sent:

- `message.new`: When a new location message is created.
- `message.updated`: When a location message is updated.

<admonition type="info">

In Dart, these events are resolved to more specific location events:

- `location.shared`: When a new location message is created.
- `location.updated`: When a location message is updated.

</admonition>

You can easily check if a message is a location message by checking the `message.sharedLocation` property. For example, you can use this events to render the locations in a map view.

<Tabs>

```swift label="Swift"
let eventsController = ChatClient.shared.eventsController()
eventsController.delegate = self

extension CustomMapView: EventsControllerDelegate {
    func eventsController(_ controller: EventsController, didReceiveEvent event: any Event) {
        /// Make sure the event is for the current channel.
        guard event.cid == currentChannelId else {
            return
        }

        if let event = event as? MessageNewEvent, let sharedLocation = event.message.sharedLocation {
            // Add the new location to the map.
        } else if let event = event as? MessageUpdatedEvent, let sharedLocation = event.message.sharedLocation { {
            // Update the location on the map.
        }
    }
}
```

```kotlin label="Kotlin"
chatClient.subscribeFor(NewMessageEvent::class, MessageUpdatedEvent::class) { event ->
    // Check if the event is for the watching channel.
    if ((event as? CidEvent)?.cid == "my-watching-cid") {
        when (event) {
            is NewMessageEvent -> event.message.sharedLocation?.let { location ->
                // Add a new location to the map.
            }
            is MessageUpdatedEvent -> event.message.sharedLocation?.let { location ->
                // Update the existing location in the map.
            }
            else -> Unit
        }
    }
}
```

```dart label="Dart"
// Listen for location-specific events
client.on(EventType.locationShared).listen((event) {
  // Handle location shared event if needed
});

client.on(EventType.locationUpdated).listen((event) {
  // Handle location updated event if needed
});
```

</Tabs>


---

This page was last updated at 2026-04-22T16:43:12.227Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/node/location_sharing/](https://getstream.io/chat/docs/node/location_sharing/).