
Mapbox Android Patterns
- 962 installs
- 71 repo stars
- Updated August 4, 2026
- mapbox/mapbox-agent-skills
mapbox-android-patterns is an official Mapbox agent skill that provides ready-to-use Kotlin and Jetpack Compose patterns for developers who need Mapbox Maps SDK v11 maps, markers, GeoJSON, styles, and camera control on A
About
mapbox-android-patterns from mapbox/mapbox-agent-skills documents official integration patterns for Mapbox Maps SDK v11 on Android using Kotlin, Jetpack Compose, and the traditional View system. The skill covers SDK installation and configuration, adding markers and annotations, showing user location with camera tracking, loading custom GeoJSON data, applying map styles, controlling camera movement, and handling featureset interactions—all aligned with official Mapbox documentation. Developers reach for mapbox-android-patterns when embedding interactive maps into Android apps instead of writing integration code from scattered docs. Patterns address common Android map tasks such as location permissions, annotation layers, and style switching without guessing API shapes. With 750 catalog installs, the skill suits mobile engineers building logistics, field, or consumer map features on Android—not iOS MapKit projects, web Mapbox GL JS-only work, or backend geospatial ETL pipelines without a mobile client.
- Official integration patterns for Mapbox Maps SDK v11 on Android
- Covers installation, markers, user location tracking, GeoJSON custom data, map styles, camera control, and feature tap h
- Supports both Jetpack Compose and traditional View system implementations
- Based directly on Mapbox Android Maps Guides and API reference
- Includes token configuration and minimum SDK requirements
Mapbox Android Patterns by the numbers
- 962 all-time installs (skills.sh)
- +33 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #244 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-android-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 962 |
|---|---|
| repo stars | ★ 71 |
| Security audit | 3 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | mapbox/mapbox-agent-skills ↗ |
How do you add Mapbox maps to Android?
Get official, ready-to-use Kotlin and Jetpack Compose patterns for adding Mapbox maps, markers, user location, styles, and feature interactions into an Android ap
Who is it for?
Android developers integrating Mapbox Maps SDK v11 with Kotlin or Jetpack Compose who want official annotation, GeoJSON, location, and camera patterns.
Skip if: iOS MapKit or Mapbox iOS SDK projects, web-only Mapbox GL JS integrations, or backend-only geospatial pipelines with no Android map UI.
When should I use this skill?
User asks to add Mapbox maps, markers, user location, GeoJSON layers, map styles, or camera control to an Android Kotlin or Jetpack Compose app.
What you get
Kotlin and Jetpack Compose Mapbox integration snippets for markers, GeoJSON layers, user location, map styles, camera control, and featureset interactions.
- Kotlin map integration snippets
- Compose map components
- GeoJSON and marker setup code
By the numbers
- Targets Mapbox Maps SDK v11 on Android
- 750 catalog installs in mapbox/mapbox-agent-skills
- Covers 6 integration areas: install, markers, location, GeoJSON, styles, camera
Files
Mapbox Android Integration Patterns
Official patterns for integrating Mapbox Maps SDK v11 on Android with Kotlin, Jetpack Compose, and View system.
Use this skill when:
- Installing and configuring Mapbox Maps SDK for Android
- Adding markers and annotations to maps
- Showing user location and tracking with camera
- Adding custom data (GeoJSON) to maps
- Working with map styles, camera, or user interaction
- Handling feature interactions and taps
Official Resources:
---
Installation & Setup
Requirements
- Android SDK 21+
- Kotlin or Java
- Android Studio
- Free Mapbox account
Step 1: Configure Access Token
Create app/res/values/mapbox_access_token.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="mapbox_access_token" translatable="false"
tools:ignore="UnusedResources">YOUR_MAPBOX_ACCESS_TOKEN</string>
</resources>Get your token: Sign in at mapbox.com
Step 2: Add Maven Repository
In settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
}
}
}Step 3: Add Dependency
In module build.gradle.kts:
android {
defaultConfig {
minSdk = 21
}
}
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
}For Jetpack Compose:
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
implementation("com.mapbox.extension:maps-compose:11.18.1")
}---
Map Initialization
Jetpack Compose Pattern
Basic map:
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import com.mapbox.maps.extension.compose.*
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
@Composable
fun MapScreen() {
MapboxMap(
modifier = Modifier.fillMaxSize()
) {
// Initialize camera via MapEffect (Style.STANDARD loads by default)
MapEffect(Unit) { mapView ->
// Set initial camera position
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
}
}
}With ornaments:
MapboxMap(
modifier = Modifier.fillMaxSize(),
scaleBar = {
ScaleBar(
enabled = true,
position = Alignment.BottomStart
)
},
compass = {
Compass(enabled = true)
}
) {
// Style.STANDARD loads by default
}View System Pattern
Layout XML (activity_map.xml):
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>Activity:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
mapView.mapboxMap.loadStyle(Style.STANDARD)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}---
Add Markers (Point Annotations)
Point annotations are the most common way to mark locations on the map.
Jetpack Compose:
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// Load style first
mapView.mapboxMap.loadStyle(Style.STANDARD)
// Create annotation manager and add markers
val annotationManager = mapView.annotations.createPointAnnotationManager()
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
annotationManager.create(pointAnnotation)
}
}
// Note: Compose doesn't have declarative PointAnnotation component
// Markers must be added imperatively via MapEffectView System:
// Create annotation manager (once, reuse for updates)
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()
// Create marker
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
pointAnnotationManager.create(pointAnnotation)Multiple markers:
val locations = listOf(
Point.fromLngLat(-122.4194, 37.7749),
Point.fromLngLat(-122.4094, 37.7849),
Point.fromLngLat(-122.4294, 37.7649)
)
val annotations = locations.map { point ->
PointAnnotationOptions()
.withPoint(point)
.withIconImage("marker")
}
pointAnnotationManager.create(annotations)---
Show User Location (Display)
Step 1: Add permissions to AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />Step 2: Request permissions and show location:
// Request permissions first (use ActivityResultContracts)
// Show location puck
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}---
Performance Best Practices
Reuse Annotation Managers
// Don't create new managers repeatedly
// val manager = mapView.annotations.createPointAnnotationManager() // each call
// Create once, reuse
val pointAnnotationManager = mapView.annotations.createPointAnnotationManager()
fun updateMarkers() {
pointAnnotationManager.deleteAll()
pointAnnotationManager.create(markers)
}Batch Annotation Updates
// Create all at once
pointAnnotationManager.create(allAnnotations)
// Don't create one by one in a loopLifecycle Management
// Always call lifecycle methods
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}Use Standard Style
// Standard style is optimized and recommended
Style.STANDARD
// Use other styles only when needed for specific use cases
Style.STANDARD_SATELLITE // Satellite imagery---
Troubleshooting
Map Not Displaying
Check:
1. Token in mapbox_access_token.xml 2. Token is valid (test at mapbox.com) 3. Maven repository configured 4. Dependency added correctly 5. Internet permission in manifest
Style Not Loading
mapView.mapboxMap.subscribeStyleLoaded { _ ->
Log.d("Map", "Style loaded successfully")
// Add layers and sources here
}Performance Issues
- Use
Style.STANDARD(recommended and optimized) - Limit visible annotations to viewport
- Reuse annotation managers
- Avoid frequent style reloads
- Call lifecycle methods (onStart, onStop, onDestroy)
- Batch annotation updates
---
Reference Files
Load these references when you need detailed patterns for specific topics:
- `references/compose.md` -- Jetpack Compose: dependencies, token setup, MapboxMap, annotations with click, GeoJSON, MapEffect
- `references/annotations.md` -- Circle, Polyline, and Polygon annotation patterns
- `references/location-tracking.md` -- Camera follow user location + get current location once
- `references/custom-data.md` -- GeoJSON sources and layers: lines, polygons, points, update/remove
- `references/camera-styles.md` -- Camera control (set, animate, fit) + map styles (built-in and custom)
- `references/interactions.md` -- Featureset interactions, custom layer taps, long press, gestures
---
Additional Resources
Mapbox Android Quick Reference
Fast reference for Mapbox Maps SDK v11 on Android with Kotlin, Jetpack Compose, and View system.
Setup
Installation (Gradle)
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
}
}
}
// build.gradle.kts
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
implementation("com.mapbox.extension:maps-compose:11.18.1") // For Compose
}Access Token
<!-- app/res/values/mapbox_access_token.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="mapbox_access_token" translatable="false"
tools:ignore="UnusedResources">YOUR_MAPBOX_ACCESS_TOKEN</string>
</resources>Jetpack Compose
Basic Map
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import com.mapbox.maps.extension.compose.*
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
@Composable
fun MapScreen() {
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
}
}
}With Annotation
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
val annotationManager = mapView.annotations.createPointAnnotationManager()
val pointAnnotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("marker")
annotationManager.create(pointAnnotation)
}
}Compose Annotations Pattern
// ❌ Declarative annotation components are not supported
// Use MapEffect with annotation managers instead (see above)
MapboxMap(modifier = Modifier.fillMaxSize()) {
// This doesn't work:
PointAnnotation(
point = Point.fromLngLat(-122.4194, 37.7749)
) {
iconImage = "custom-marker"
}
}View System
Basic Map
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.geojson.Point
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-122.4194, 37.7749))
.zoom(12.0)
.build()
)
mapView.mapboxMap.loadStyle(Style.STANDARD)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}Common Patterns
1. Add Markers
val manager = mapView.annotations.createPointAnnotationManager()
val annotation = PointAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withIconImage("custom-marker")
manager.create(annotation)2. User Location with Camera Follow
// Request permission (add to AndroidManifest.xml)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
// Show user location
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}
// Follow user location with camera
mapView.location.addOnIndicatorPositionChangedListener { point ->
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(15.0)
.pitch(45.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
// Optional: Follow bearing (direction)
mapView.location.addOnIndicatorBearingChangedListener { bearing ->
mapView.camera.easeTo(
CameraOptions.Builder()
.bearing(bearing)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}3. Add Custom Data (GeoJSON)
val geoJsonSource = geoJsonSource("route-source") {
geometry(LineString.fromLngLats(coordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val lineLayer = lineLayer("route-layer", "route-source") {
lineColor(Color.BLUE)
lineWidth(4.0)
}
mapView.mapboxMap.style?.addLayer(lineLayer)4. Camera Control
// Fly animation
mapView.camera.flyTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(2000)
.build()
)
// Ease animation
mapView.camera.easeTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)5. Featureset Interactions
import com.mapbox.maps.interactions.ClickInteraction
// Tap on POI features
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true // Stop propagation
}
)
// Tap on buildings
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
// Highlight the building
mapView.mapboxMap.setFeatureState(
building,
StandardBuildingsState {
highlight(true)
}
)
true
}
)6. Map Tap Handling
mapView.gestures.addOnMapClickListener { point ->
Log.d("MapClick", "Tapped at: ${point.latitude()}, ${point.longitude()}")
true // Consume event
}7. Styles
// Compose
MapboxMap(style = Style.STANDARD) // Recommended
MapboxMap(style = Style.DARK)
MapboxMap(style = Style.STANDARD_SATELLITE)
// Views
mapView.mapboxMap.loadStyle(Style.STANDARD)
mapView.mapboxMap.loadStyle(Style.DARK)Performance Tips
Reuse Managers
// ✅ Create once
val annotationManager = mapView.annotations.createPointAnnotationManager()
// ✅ Update many times
fun updateMarkers() {
annotationManager.deleteAll()
annotationManager.create(newMarkers)
}Batch Updates
// ✅ Create all at once
pointAnnotationManager.create(allAnnotations)
// ❌ Don't create one by one
allAnnotations.forEach { annotation ->
pointAnnotationManager.create(annotation)
}Lifecycle Management
// Always call lifecycle methods
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}Use Standard Style
// ✅ Recommended
Style.STANDARD
// Use others only when needed
Style.STANDARD_SATELLITEQuick Checklist
✅ Token in mapbox_access_token.xml ✅ Maven repository configured ✅ MapboxMaps dependency added ✅ Location permissions if needed ✅ Use Style.STANDARD (recommended) ✅ Lifecycle methods called ✅ Annotation managers reused
Resources
{
"skill_name": "mapbox-android-patterns",
"evals": [
{
"id": 1,
"prompt": "I'm setting up the Mapbox Maps SDK in my Android app. How do I add the dependency and configure the access token?",
"expectations": [
"Must add the Mapbox Maven repository in settings.gradle.kts: url = uri(\"https://api.mapbox.com/downloads/v2/releases/maven\")",
"Add the dependency: implementation(\"com.mapbox.maps:android:11.x.x\")",
"Token goes in a resources file app/res/values/mapbox_access_token.xml with the string resource name mapbox_access_token",
"Shows the correct XML: <string name=\"mapbox_access_token\">YOUR_TOKEN</string>"
]
},
{
"id": 2,
"prompt": "My Android app creates a new `PointAnnotationManager` inside my `refreshMarkers()` method that gets called on every data update. Performance is noticeably poor. What should I change?",
"expectations": [
"Creating annotation managers repeatedly is the problem — each call reinitializes resources",
"Create the PointAnnotationManager once (e.g., in onCreate or a setup method) and reuse it",
"To update markers, reassign the annotations list: pointAnnotationManager.annotations = newAnnotations",
"Recommends batch updates: set all annotations at once rather than appending individually"
]
},
{
"id": 3,
"prompt": "I'm using the Mapbox Standard style on Android and want to handle user taps on buildings. When a user taps a building, I want to highlight it. How do I implement this?",
"expectations": [
"Use ClickInteraction.standardBuildings { building, context -> ... } — the modern Interactions API for Standard style featuresets",
"Shows the pattern: mapView.mapboxMap.addInteraction(ClickInteraction.standardBuildings { building, context -> ... })",
"To highlight, use setFeatureState on the building: mapView.mapboxMap.setFeatureState(building, StandardBuildingsState { highlight(true) })",
"Notes that returning true from the lambda stops propagation to other interactions"
]
},
{
"id": 4,
"prompt": "Add a Mapbox map to my Android Jetpack Compose project. I need the correct dependencies and a basic MapboxMap composable.",
"expectations": [
"Compose extension dependency uses group com.mapbox.extension, not com.mapbox.maps: implementation(\"com.mapbox.extension:maps-compose:11.x.x\")",
"Must NOT use com.mapbox.maps:compose, com.mapbox.maps:extension-compose, or com.mapbox.maps:android-compose",
"Both maps SDK and compose extension must use the same version",
"Uses MapboxMap composable from com.mapbox.maps.extension.compose, not AndroidView wrapping MapView"
]
},
{
"id": 5,
"prompt": "I need to set the Mapbox access token programmatically in my Compose app before the map loads. What's the correct import and call?",
"expectations": [
"Import is from com.mapbox.common.MapboxOptions, NOT com.mapbox.maps.MapboxOptions",
"Call MapboxOptions.accessToken = \"pk.your_token\" before any map component is created",
"Recommends mapbox_access_token string resource as the preferred alternative",
"Must NOT use manifest <meta-data> placeholders with ${MAPBOX_ACCESS_TOKEN}"
]
},
{
"id": 6,
"prompt": "I want to add tappable point markers to my Mapbox Compose map. When a marker is tapped, I need to know which one was clicked. Show me the correct pattern.",
"expectations": [
"Uses PointAnnotation composable from com.mapbox.maps.extension.compose.annotation.generated",
"Uses interactionsState.onClicked { ... } inside the init lambda for tap handling, NOT the deprecated onClick parameter",
"Uses rememberIconImage(R.drawable.ic_marker) for the marker icon, not BitmapFactory",
"PointAnnotation renders nothing without iconImage — must always set it"
]
}
]
}
Annotations: Circle, Polyline, Polygon
Additional annotation types beyond point markers.
Circle Annotations
val circleAnnotationManager = mapView.annotations.createCircleAnnotationManager()
val circle = CircleAnnotationOptions()
.withPoint(Point.fromLngLat(-122.4194, 37.7749))
.withCircleRadius(10.0)
.withCircleColor("#FF0000")
circleAnnotationManager.create(circle)Polyline Annotations
val polylineAnnotationManager = mapView.annotations.createPolylineAnnotationManager()
val polyline = PolylineAnnotationOptions()
.withPoints(listOf(point1, point2, point3))
.withLineColor("#0000FF")
.withLineWidth(4.0)
polylineAnnotationManager.create(polyline)Polygon Annotations
val polygonAnnotationManager = mapView.annotations.createPolygonAnnotationManager()
val points = listOf(listOf(coord1, coord2, coord3, coord1)) // Close the polygon
val polygon = PolygonAnnotationOptions()
.withPoints(points)
.withFillColor("#0000FF")
.withFillOpacity(0.5)
polygonAnnotationManager.create(polygon)Camera Control + Map Styles
Camera Control
Set Camera Position
// Compose - Update camera state
cameraState.position = CameraPosition(
center = Point.fromLngLat(-74.0060, 40.7128),
zoom = 14.0,
bearing = 90.0,
pitch = 60.0
)
// Views - Immediate
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(Point.fromLngLat(-74.0060, 40.7128))
.zoom(14.0)
.bearing(90.0)
.pitch(60.0)
.build()
)Animated Camera Transitions
// Fly animation (dramatic arc)
mapView.camera.flyTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(2000)
.build()
)
// Ease animation (smooth)
mapView.camera.easeTo(
CameraOptions.Builder()
.center(destination)
.zoom(15.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)Fit Camera to Coordinates
val coordinates = listOf(coord1, coord2, coord3)
val camera = mapView.mapboxMap.cameraForCoordinates(
coordinates,
EdgeInsets(50.0, 50.0, 50.0, 50.0),
bearing = 0.0,
pitch = 0.0
)
mapView.camera.easeTo(camera)Map Styles
Built-in Styles
// Compose - load style via MapEffect
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// Style.STANDARD loads by default, explicit loading only needed for other styles
// mapView.mapboxMap.loadStyle(Style.STREETS) // Mapbox Streets
// mapView.mapboxMap.loadStyle(Style.OUTDOORS) // Mapbox Outdoors
// mapView.mapboxMap.loadStyle(Style.LIGHT) // Mapbox Light
// mapView.mapboxMap.loadStyle(Style.DARK) // Mapbox Dark
// mapView.mapboxMap.loadStyle(Style.STANDARD_SATELLITE) // Satellite imagery
// mapView.mapboxMap.loadStyle(Style.SATELLITE_STREETS) // Satellite + streets
}
}
// Views
mapView.mapboxMap.loadStyle(Style.STANDARD)
mapView.mapboxMap.loadStyle(Style.DARK)Custom Style URL
val customStyleUrl = "mapbox://styles/username/style-id"
// Compose
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
mapView.mapboxMap.loadStyle(customStyleUrl)
}
}
// Views
mapView.mapboxMap.loadStyle(customStyleUrl)Jetpack Compose Integration
Compose-specific patterns for Mapbox Maps SDK v11.
Dependencies
dependencies {
implementation("com.mapbox.maps:android:11.18.1")
implementation("com.mapbox.extension:maps-compose:11.18.1")
}Check releases for the latest version. Both artifacts must use the same version.
The Compose extension group is com.mapbox.extension, NOT com.mapbox.maps. These do NOT exist: com.mapbox.maps:compose, com.mapbox.maps:extension-compose, com.mapbox.maps:android-compose, com.mapbox.maps:maps-compose.
Access Token
Create app/src/main/res/values/mapbox_access_token.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="mapbox_access_token" translatable="false"
tools:ignore="UnusedResources">YOUR_MAPBOX_ACCESS_TOKEN</string>
</resources>The SDK reads this resource automatically. This is the recommended approach.
To set the token programmatically, import from com.mapbox.common (NOT com.mapbox.maps):
import com.mapbox.common.MapboxOptions
MapboxOptions.accessToken = "pk.your_token"Do NOT use manifest <meta-data> placeholders — the SDK does not read tokens from ${MAPBOX_ACCESS_TOKEN} in AndroidManifest.xml.
MapboxMap Composable
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState
import com.mapbox.geojson.Point
@Composable
fun MapScreen() {
MapboxMap(
modifier = Modifier.fillMaxSize(),
mapViewportState = rememberMapViewportState {
setCameraOptions {
center(Point.fromLngLat(-122.4194, 37.7749))
zoom(12.0)
}
}
)
}rememberMapViewportState is in com.mapbox.maps.extension.compose.animation.viewport, not the root compose package.
Point Annotations
import com.mapbox.maps.extension.compose.annotation.generated.PointAnnotation
import com.mapbox.maps.extension.compose.annotation.rememberIconImage
@Composable
fun MapWithMarkers() {
val markerIcon = rememberIconImage(R.drawable.ic_marker)
MapboxMap(modifier = Modifier.fillMaxSize(), mapViewportState = ...) {
PointAnnotation(point = Point.fromLngLat(lng, lat)) {
iconImage = markerIcon
interactionsState.onClicked { /* handle tap */ true }
}
}
}PointAnnotationrenders nothing withouticonImage— always set it- Use
rememberIconImage(R.drawable.ic_marker)for drawable resources - Use
interactionsState.onClicked { ... }for tap handling (theonClickparameter is deprecated) - Annotation IDs are
Long, notString
Loading GeoJSON from Assets
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point
val json = context.assets.open("data.geojson").bufferedReader().use { it.readText() }
val features = FeatureCollection.fromJson(json).features() ?: emptyList()
features.forEach { feature ->
val point = feature.geometry() as Point
val name = feature.getStringProperty("name")
}MapEffect
Use MapEffect for imperative style and layer operations (prefer PointAnnotation composable for markers):
import com.mapbox.maps.extension.compose.MapEffect
MapboxMap(...) {
MapEffect(Unit) { mapView ->
// Style operations, custom layers, etc.
}
}Import is com.mapbox.maps.extension.compose.MapEffect, not LaunchedEffect from Compose runtime.
GeoJSON: Lines, Polygons, Points, Update/Remove
Add your own data to the map using GeoJSON sources and layers.
Add Line (Route, Path)
// Create coordinates for the line
val routeCoordinates = listOf(
Point.fromLngLat(-122.4194, 37.7749),
Point.fromLngLat(-122.4094, 37.7849),
Point.fromLngLat(-122.3994, 37.7949)
)
// Create GeoJSON source
val geoJsonSource = geoJsonSource("route-source") {
geometry(LineString.fromLngLats(routeCoordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
// Create line layer
val lineLayer = lineLayer("route-layer", "route-source") {
lineColor(Color.BLUE)
lineWidth(4.0)
lineCap(LineCap.ROUND)
lineJoin(LineJoin.ROUND)
}
mapView.mapboxMap.style?.addLayer(lineLayer)Add Polygon (Area)
val polygonCoordinates = listOf(
listOf(coord1, coord2, coord3, coord1) // Close the polygon
)
val geoJsonSource = geoJsonSource("area-source") {
geometry(Polygon.fromLngLats(polygonCoordinates))
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val fillLayer = fillLayer("area-fill", "area-source") {
fillColor(Color.parseColor("#0000FF"))
fillOpacity(0.3)
fillOutlineColor(Color.parseColor("#0000FF"))
}
mapView.mapboxMap.style?.addLayer(fillLayer)Add Points from GeoJSON
val geojsonString = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4194, 37.7749]},
"properties": {"name": "Location 1"}
},
{
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [-122.4094, 37.7849]},
"properties": {"name": "Location 2"}
}
]
}
"""
val geoJsonSource = geoJsonSource("points-source") {
data(geojsonString)
}
mapView.mapboxMap.style?.addSource(geoJsonSource)
val symbolLayer = symbolLayer("points-layer", "points-source") {
iconImage("marker")
textField(Expression.get("name"))
textOffset(listOf(0.0, 1.5))
}
mapView.mapboxMap.style?.addLayer(symbolLayer)Update Layer Properties
mapView.mapboxMap.style?.getLayerAs<LineLayer>("route-layer")?.let { layer ->
layer.lineColor(Color.RED)
layer.lineWidth(6.0)
}Remove Layers and Sources
mapView.mapboxMap.style?.removeStyleLayer("route-layer")
mapView.mapboxMap.style?.removeStyleSource("route-source")Featureset Interactions, Custom Layer Taps, Long Press, Gestures
Featureset Interactions (Recommended)
The modern Interactions API allows handling taps on map features with typed feature access. Works with Standard Style predefined featuresets like POIs, buildings, and place labels.
View System Pattern:
import com.mapbox.maps.interactions.ClickInteraction
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.loadStyle(Style.STANDARD)
setupFeatureInteractions()
}
private fun setupFeatureInteractions() {
// Tap on POI features
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true // Stop propagation
}
)
// Tap on buildings
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
// Highlight the building
mapView.mapboxMap.setFeatureState(
building,
StandardBuildingsState {
highlight(true)
}
)
true
}
)
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}Jetpack Compose Pattern:
@Composable
fun MapScreen() {
MapboxMap(modifier = Modifier.fillMaxSize()) {
MapEffect(Unit) { mapView ->
// Load Standard style
mapView.mapboxMap.loadStyle(Style.STANDARD)
// Add featureset interactions using View system API
mapView.mapboxMap.addInteraction(
ClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Tapped POI: ${poi.name}")
true
}
)
mapView.mapboxMap.addInteraction(
ClickInteraction.standardBuildings { building, context ->
Log.d("MapTap", "Tapped building")
mapView.mapboxMap.setFeatureState(
building,
state = mapOf("select" to true)
)
true
}
)
}
}
}
// Note: Featureset interactions in Compose use MapEffect to access
// the underlying MapView and use the View system interaction APITap on Custom Layers
mapView.mapboxMap.addInteraction(
ClickInteraction.layer("custom-layer-id") { feature, context ->
Log.d("MapTap", "Feature properties: ${feature.properties()}")
true
}
)Long Press Interactions
import com.mapbox.maps.interactions.LongClickInteraction
mapView.mapboxMap.addInteraction(
LongClickInteraction.standardPoi { poi, context ->
Log.d("MapTap", "Long pressed POI: ${poi.name}")
true
}
)Handle Map Clicks (Empty Space)
mapView.gestures.addOnMapClickListener { point ->
Log.d("MapClick", "Tapped at: ${point.latitude()}, ${point.longitude()}")
true // Consume event
}Gesture Configuration
// Disable specific gestures
mapView.gestures.pitchEnabled = false
mapView.gestures.rotateEnabled = false
// Configure zoom limits
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.zoom(12.0)
.build()
)Camera Follow User + Get Current Location
Camera Follow User Location
To make the camera follow the user's location as they move:
class MapActivity : AppCompatActivity() {
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
mapView = findViewById(R.id.mapView)
mapView.mapboxMap.loadStyle(Style.STANDARD)
setupLocationTracking()
}
private fun setupLocationTracking() {
// Request permissions first (use ActivityResultContracts)
// Show user location
mapView.location.updateSettings {
enabled = true
puckBearingEnabled = true
}
// Follow user location with camera
mapView.location.addOnIndicatorPositionChangedListener { point ->
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(15.0)
.pitch(45.0)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
// Optional: Follow bearing (direction) as well
mapView.location.addOnIndicatorBearingChangedListener { bearing ->
mapView.camera.easeTo(
CameraOptions.Builder()
.bearing(bearing)
.build(),
MapAnimationOptions.Builder()
.duration(1000)
.build()
)
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}Get Current Location Once
mapView.location.getLastLocation { location ->
location?.let {
val point = Point.fromLngLat(it.longitude, it.latitude)
mapView.camera.easeTo(
CameraOptions.Builder()
.center(point)
.zoom(14.0)
.build()
)
}
}Related skills
How it compares
Pick mapbox-android-patterns for official Mapbox Android SDK snippets; use generic mobile UI skills when maps are not part of the integration scope.
FAQ
Which Mapbox SDK version does mapbox-android-patterns target?
mapbox-android-patterns documents official integration patterns for Mapbox Maps SDK v11 on Android, covering Kotlin, Jetpack Compose, and the View system per the mapbox/mapbox-agent-skills README.
What Android map features does the skill cover?
mapbox-android-patterns includes installation, markers and annotations, user location with camera tracking, custom GeoJSON data, map styles, camera control, and featureset interactions from official Mapbox documentation.
Does mapbox-android-patterns support Jetpack Compose?
mapbox-android-patterns provides Jetpack Compose integration patterns alongside Kotlin View-system examples for Mapbox Maps SDK v11, so Compose-first Android apps can embed maps without guessing API usage.
Is Mapbox Android Patterns safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.