# Maps Compose is the Compose layer over the Maps SDK for Android

> android-maps-compose is Google's own set of Jetpack Compose components for the Maps SDK for Android, published as three artifacts at one version, with a configuration model that splits runtime properties, UI settings and one-time map options into three separate places. It is the supported path to putting a map in a Compose app, and the camera rules are where most integrations go wrong.

**googlemaps/android-maps-compose** — Jetpack Compose composables for the Maps SDK for Android

- Repository: https://github.com/googlemaps/android-maps-compose
- Website: https://developers.google.com/maps/documentation/android-sdk/maps-compose
- Stars: 1,320 · Forks: 185
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/googlemaps-android-maps-compose

## The first party bridge between Compose and the Maps SDK

Maps Compose contains Jetpack Compose components for the Maps SDK for Android, and it lives in the googlemaps organisation rather than in a community repository, which is the main reason to use it over an alternative wrapper.

The problem it solves is narrow and real. The Maps SDK for Android is a View based library, with a MapView, an Options object and a set of listener callbacks. A Compose application that wants a map has three choices, and only one of them is pleasant. You can embed the MapView in Compose through an AndroidView interop wrapper and translate every callback by hand, you can keep the map outside the Compose tree and fight the two lifecycles against each other, or you can use a component set that has already done that work.

The component set is a thin layer, and the readme shows it. A map is a composable that takes a modifier, and the state around it is ordinary Compose state held with remember and mutableStateOf. The camera is a CameraPositionState you remember, the configuration is a MapProperties object you copy to change, and the drawing is child composables inside the map.

The project publishes to Maven Central, the licence is Apache-2.0, and the last push was on 2026-10-15 with releases v9.0.1 on 2026-10-08 and v9.0.0 on 2026-10-01, after a v9.0.0-rc03 candidate on 2026-09-15.

## Three artifacts published at one version

The installation section notes a change that removes a common source of version conflicts. You no longer need to specify the Maps SDK for Android or its Utility Library as separate dependencies, since maps-compose and maps-compose-utils pull in the appropriate versions of these respectively.

Three artifacts are published, all with the same version number.

```groovy
dependencies {
    implementation 'com.google.maps.android:maps-compose:9.0.1' // {x-release-please-version}
    // Optionally, you can include the Compose utils library for Clustering,
    // Street View metadata checks, etc.
    implementation 'com.google.maps.android:maps-compose-utils:9.0.1' // {x-release-please-version}
    // Optionally, you can include the widgets library for ScaleBar, etc.
    implementation 'com.google.maps.android:maps-compose-widgets:9.0.1' // {x-release-please-version}
}
```

The split is by capability rather than by layer. maps-compose is the map itself, maps-compose-utils holds the extras such as clustering and Street View metadata checks, and maps-compose-widgets holds the decorative components such as the ScaleBar. Each is optional except the first, which is the point: an app that draws a map and nothing else should not carry a widget library.

The version comment in the dependency block, {x-release-please-version}, is a marker rather than code. It is what the release tooling replaces, and the repository holds a .release-please-manifest.json and a release-please-config.json, so the version in your build file is updated automatically when a release is published. The readme tells you which version of Jetpack Compose you need by pointing at the releases page.

## Adding a map takes a composable and a remembered camera

The usage section gives the smallest complete example, and it is worth reading as a template because the shape of it does not change for anything else you add.

```kotlin
val singapore = LatLng(1.35, 103.87)
val cameraPositionState = rememberCameraPositionState {
    position = CameraPosition.fromLatLngZoom(singapore, 10f)
}
GoogleMap(
    modifier = Modifier.fillMaxSize(),
    cameraPositionState = cameraPositionState
)
```

Three things happen there. A coordinate is defined, a camera position state is remembered with an initial position and zoom, and the GoogleMap composable is given that state.

The remember call is what makes the camera survive recomposition. A camera position state created outside a remember would be a new object on every recomposition, and the map would follow it back to its initial position, which is the single most common symptom of a camera that resets whenever the UI updates.

The requirements the readme lists before any of this matter. You need Android API level 21 or newer, a Kotlin-enabled project, a Jetpack Compose-enabled project, a sign up with Google Maps Platform, a Google Maps Platform project with the Maps SDK for Android enabled, and an API key associated with that project.

## Three places to configure the map, and which one is which

The configuration model is the part of the library that will save you from the most common mistake, which is putting a setting in the wrong container and finding that it does nothing.

MapProperties and MapUiSettings should be your first go-to for configuring the map. MapProperties holds the settings that can change while the map is alive, and because it is a data class you copy it to change a value, which is what makes the map recompose with the new setting.

```kotlin
var mapProperties by remember {
    mutableStateOf(
        MapProperties(maxZoomPreference = 10f, minZoomPreference = 5f)
    )
}
```

The distinction is that MapProperties covers the map and MapUiSettings covers the user interface, such as whether the map toolbar is enabled. The example toggles isBuildingEnabled on the properties and mapToolbarEnabled on the UI settings, with a button for each.

The third place is for everything else. For any configuration not present in those two classes, use googleMapOptionsFactory to provide a GoogleMapOptions instance instead. The rule for which is which is stated plainly: anything that can only be provided once, when the map is created, such as the map ID, should be provided via googleMapOptionsFactory, and the example passes GoogleMapOptions().mapId("MyMapId") that way.

## The camera has one source of truth and a load order

Camera changes and updates can be observed and controlled via CameraPositionState, and the readme is emphatic about one rule.

CameraPositionState is the source of truth for anything camera related. So, providing a camera position in GoogleMapOptions will be overridden by CameraPosition.

That single sentence prevents a whole class of confusion, because it means there are not two places to set the camera and pick a winner. There is one, and anything you set at creation time in the options is ignored.

The second rule is about timing. Remember that the map must load before any camera state can be set. If you are using a LaunchedEffect, you must wait until the map has been loaded, and the documented pattern is to track a loaded flag in state, set it from the onMapLoaded callback, and only then run the effect.

```kotlin
GoogleMap(
    modifier = Modifier.fillMaxSize(),
    onMapLoaded = { mapLoaded = true }
)
```

The reason for the rule is not hidden. A camera move issued before the map surface exists has nothing to act on, so it is either dropped or applied to a map that then resets itself once it loads. The same applies to the user, who can pan and zoom the map by hand, and the state object is where those changes are observed, so an app that wants to know where the user is looking reads the state rather than tracking gestures itself.

## Drawing on the map is just more composables

The Drawing on a map section starts with the sentence that defines the approach. Drawing on the map, such as adding markers, can be accomplished by adding child composable elements.

That is a different model from the View based SDK, where markers are added to a list of options and the map redraws. Here, a marker is a composable inside the GoogleMap composable, and it participates in the normal Compose lifecycle. That means a marker can be driven by state, animated, removed by changing the composition, and laid out with the same modifiers as anything else in the tree.

The consequence is that the number of markers is a rendering cost decided by the composition rather than a list you rebuild, and that anything you do to a marker is a recomposition rather than a call into the SDK. For an app with a handful of pins and some state, that is exactly right. For an app with thousands of points, the usual answer is to keep them in a layer rather than in the composition, and the clustering support in the utils artifact is the tool for that.

The repository layout supports the same three-way split as the published artifacts. There are maps-compose/, maps-compose-utils/ and maps-compose-widgets/ directories alongside the sample application in maps-app/, plus build-logic/ for the shared Gradle convention plugins and a settings.gradle.kts that ties the modules together.

## How the releases and the sample app are wired

The sample app is part of the repository, in the maps-app/ directory, and the instructions for running it are worth following because they show the intended handling of the API key.

You open the secrets.properties file in your top level directory and add the key, replacing YOUR_API_KEY with your own, and the file has to sit next to the local.defaults.properties file. The reason given is that secrets.properties is excluded from being checked into a version control system, and if the file does not exist you create it in the same place.

```
MAPS_API_KEY=YOUR_API_KEY
```

That is the whole credential story, and it is a small file that the readme tells you not to commit. The repository keeps local.defaults.properties at the top level for the non secret defaults, and the split between the two is the pattern most projects adopt and few document.

The release process is automated. The repository carries a Release badge on the GitHub Actions workflows, a .release-please-manifest.json and a release-please-config.json, and the version placeholders in the build files are what that tooling rewrites. The version comments in the dependency snippet are the visible trace of it.

The quality signals are also visible. There is a Tests/Build workflow badge and an instrumented coverage badge that reads from a coverage-history branch, which is the more honest way to publish coverage for a library whose meaningful tests need a device, since instrumentation tests do not run in a normal CI container.

## Conclusion

Adopt Maps Compose when your Android app already uses Jetpack Compose and needs a Google map, since it is the first party component set for the Maps SDK and it keeps the SDK and Utility Library versions aligned for you. Do not adopt it for a view based XML app that has no Compose, since the whole surface is composables. Verify first that your project meets the stated requirements, Android API level 21 or newer, a Kotlin-enabled and Compose-enabled project, and a Maps Platform project with the Maps SDK for Android enabled and an API key, and remember that the map must finish loading before any camera state is set.

## FAQ

### What is the Maps Compose library?

It contains Jetpack Compose components for the Maps SDK for Android, published by Google as com.google.maps.android:maps-compose. It lets a Compose app add a GoogleMap composable with a remembered camera state, configuration objects and composable children for drawing on the map.

### Which dependencies do I need for Maps Compose?

The Maps SDK for Android and its Utility Library no longer have to be declared separately, since maps-compose and maps-compose-utils pull in the appropriate versions. You can also add maps-compose-utils for clustering and Street View metadata checks, and maps-compose-widgets for the ScaleBar and similar components.

### What are the requirements for Maps Compose?

Android API level 21 or newer, a Kotlin-enabled project, a Jetpack Compose-enabled project, a sign up with Google Maps Platform, a Google Maps Platform project with the Maps SDK for Android enabled, and an API key associated with that project.

### When should I use googleMapOptionsFactory instead of MapProperties?

MapProperties and MapUiSettings are the first choice for configuring the map, and anything that can only be provided once when the map is created, such as the map ID, should be provided through googleMapOptionsFactory instead.

### Why does my camera reset when the map loads?

CameraPositionState is the source of truth for anything camera related, so a position given in GoogleMapOptions is overridden by the camera position state. The map must also load before any camera state can be set, so wait for onMapLoaded before running camera operations in a LaunchedEffect.

### How do I add markers to a map in Maps Compose?

By adding child composable elements inside the GoogleMap composable, rather than by adding to a list of options as in the view based SDK. The repository includes a sample app in the maps-app/ directory that shows the pattern in use.

## Sources

- [googlemaps/android-maps-compose on GitHub](https://github.com/googlemaps/android-maps-compose)
- [License: Apache-2.0](https://github.com/googlemaps/android-maps-compose/blob/main/LICENSE)
- [Project website](https://developers.google.com/maps/documentation/android-sdk/maps-compose)
- [README](https://github.com/googlemaps/android-maps-compose/blob/main/README.md)
- [Releases](https://github.com/googlemaps/android-maps-compose/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/googlemaps-android-maps-compose
