Skip to content
This site is a preview of the main branch. The published documentation is at maplibre.org.

Getting started

You need a Compose Multiplatform project. To create one, follow the JetBrains guide or run kotlin init compose-multiplatform.

The library is on Maven Central. The latest release is v0.20.0. Add it to your version catalog:

libs.versions.toml
[libraries]
maplibre-compose = { module = "org.maplibre.compose:maplibre-compose", version = "0.20.0" }

Then add it to the shared source set:

build.gradle.kts
commonMain.dependencies {
implementation(libs.maplibre.compose)
}

Complete the section for each target that your app supports.

Some targets need a runtime artifact, which contains the native map engine for one graphics API. The steps below pick one runtime per target; Platform setup lists the other options.

Add the Vulkan runtime to the Android app:

build.gradle.kts
androidMain.dependencies {
runtimeOnly("org.maplibre.compose:maplibre-compose-runtime-vulkan-android:0.20.0")
}

iOS needs no runtime artifact. The app must target iOS 15.5 or later.

Add these lines to Other Linker Flags in your iOS app target’s build settings:

-l"c++"
-lz
-framework CoreFoundation
-framework CoreGraphics
-framework CoreText
-framework Foundation
-framework ImageIO
-framework Metal
-framework QuartzCore

Desktop apps run on Java 25 or later. These steps use a Compose Desktop window, which runs on AWT. Platform setup covers other windows, including Nucleus, which we recommend.

Add the runtime for each operating system and CPU you ship, and kotlinx-coroutines-swing, which supplies the main thread that maps deliver their events on. This example is for Linux on x64:

build.gradle.kts
jvmMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.11.0")
runtimeOnly("org.maplibre.compose:maplibre-compose-runtime-vulkan-linux-x64:0.20.0")
}

The map engine calls native code through Java’s foreign function API, so the JVM must start with --enable-native-access=ALL-UNNAMED:

build.gradle.kts
compose.desktop {
application {
jvmArgs += "--enable-native-access=ALL-UNNAMED"
}
}

A map draws with the graphics context of its window. Wrap the content of each window that shows a map in ProvideMapPresentationHost, and pass it a host from rememberAwtComposeMapPresentationHost:

Main.kt
fun main() {
singleWindowApplication {
ProvideMapPresentationHost(host = rememberAwtComposeMapPresentationHost(window)) {
App()
}
}
}

Native macOS (the macosArm64 target) is experimental in Compose. Turn it on in gradle.properties:

gradle.properties
org.jetbrains.compose.experimental.macos.enabled=true

Wrap the content of each Window that shows a map in ProvideMapPresentationHost:

fun openMapWindow() {
Window("My app", DpSize(800.dp, 600.dp)) {
ProvideMapPresentationHost(window) { App() }
}
}

Build the JS target as ES modules:

build.gradle.kts
kotlin {
js {
useEsModules()
browser()
}
}

Call installMaplibreCompose inside onWasmReady, before ComposeViewport. Without it, maps load but never appear on screen.

main.kt
fun main() {
onWasmReady {
installMaplibreCompose()
ComposeViewport(document.body!!) { App() }
}
}

Add a map to your shared UI:

MyMap.kt
@Composable
fun MyMap() {
val mapState =
rememberMapState(
baseStyle = BaseStyle.Uri("https://tiles.openfreemap.org/styles/liberty"),
initialCameraPosition =
CameraPosition(center = Position(latitude = 45.521, longitude = -122.675), zoom = 13.0),
)
MaplibreMap(state = mapState)
}

rememberMapState creates the map’s state. baseStyle loads a style from a URL. The style decides what the map shows and how it looks. This example uses the free OpenFreeMap Liberty style. If you leave out baseStyle, the map shows the MapLibre demo style, which has little detail. initialCameraPosition is where the map opens: here, downtown Portland at zoom 13.

MaplibreMap fills all the space its parent gives it. Users can pan, zoom, rotate, and tilt the map with touch gestures or a mouse.

Next, choose a style or move the camera.