Skip to content

Compose Multiplatform

Compose Multiplatform is a declarative UI framework developed by JetBrains for sharing Kotlin UI code across multiple platforms.

Enabling Compose

Use settings.compose.enabled to enable Compose:

product: jvm/app

dependencies:
  - $compose.desktop.currentOs # (1)!

settings: 
  compose: enabled # (2)!
  1. This library is from the built-in $compose catalog, accessible when Compose is enabled
  2. Enables the Compose toolchain (compiler plugin and runtime dependencies)
product: android/app

dependencies:
  - $compose.foundation # (1)!
  - $compose.material3

settings:
  android:
    applicationId: com.example.app
  compose: enabled # (2)!
  1. These libraries are from the built-in $compose catalog, accessible when Compose is enabled
  2. Enables the Compose toolchain (compiler plugin and runtime dependencies)
product:
  type: kmp/lib
  platforms: [ jvm, android, iosSimulatorArm64, iosArm64 ]

dependencies:
  - $compose.foundation: exported # (1)!
  - $compose.material3: exported

settings:
  compose:
    enabled: true # (2)!
  1. These libraries are from the built-in $compose catalog, accessible when Compose is enabled. We export them to consumers because we use types from these libraries in our own public API.
  2. Enables the Compose toolchain (compiler plugin and runtime dependencies)

Enabling Compose does the following:

  • configures the Compose compiler plugin for the Kotlin compiler
  • adds the required org.jetbrains.compose.runtime:runtime and org.jetbrains.compose.components:components-resources dependencies (implicitly)
  • enables the built-in $compose.* library catalog for all optional Compose modules

Custom Compose version

You can specify the exact version of the Compose framework to use this way:

settings:
  compose:
    enabled: true
    version: 1.5.10

Using multiplatform resources

The Kotlin Toolchain supports Compose Multiplatform resources.

Place your resources in the composeResources folder at the root of your module:

my-kmp-module/
├─ src/
│  ╰─ ... # (1)!
├─ composeResources/
│  ├─ values/
│  │  ╰─ strings.xml
│  ╰─ drawable/
│     ╰─ image.jpg
╰─ module.yaml

  1. Your Kotlin code is here

The Kotlin Toolchain then automatically generates the Kotlin accessors for these resources, and you can use them in your Kotlin code:

import my_kmp_module.generated.resources.Res
import my_kmp_module.generated.resources.hello
// other imports

@Composable
private fun displayHelloText() {
    BasicText(stringResource(Res.string.hello))
}

Read more about setting up and using compose resources in the documentation.

Generated accessors package

By default, resources accessors are generated in the package <sanitized-module-name>.generated.resources. Names are sanitized by lowercasing them, replacing - characters with _, and prefixing them with _ if they start with a digit.

In the above example, where the module name is my-kmp-module, the package name for the generated resources is therefore my_kmp_module.generated.resources.

You can customize the package name by setting the settings.compose.resources.packageName property in your module file:

settings:
  compose:
    resources:
      packageName: com.example.gen

Modules published to a Maven repository

Libraries usually declare publication coordinates in the settings.publishing section. When a group is declared there, it is included in the default package, which becomes <sanitized-group>.<sanitized-artifact-id>.generated.resources, where <sanitized-artifact-id> defaults to the module name, just like the published coordinates do. This is the naming scheme of the Compose Multiplatform Gradle plugin, so such a module gets the same package as it would in a Gradle build, as long as it keeps the default artifactId. Modules without a group still differ from Gradle, though: Gradle derives a default group from the root project name and the project path, while the Kotlin Toolchain uses the module name alone.

Customizing Res object name

By default, generated resources accessors are extension of the <resources-package-name>.Res object. You can customize the name of this object via the settings.compose.resources.nameOfResClass property:

settings:
  compose:
    resources:
      nameOfResClass: Resources
Then the resources can be accessed like Resources.string.hello instead of Res.string.hello.

Making resource accessors public

Generated Res object and the accessors are internal by default, so they are only visible in the module that has the resources. If you want to expose your resources to the consumers of your module and make them part of the module's API, you can use settings.compose.resources.exposedAccessors property:

settings:
  compose:
    resources:
      exposedAccessors: true
This will make the generated accessors and the Res class public.

Compose Hot Reload

The Kotlin Toolchain supports Compose Hot Reload, allowing you to see UI changes in real-time without restarting the application. This significantly improves the developer experience by shortening the feedback loop during UI development.

Hot-run your application

To run your application with Compose Hot Reload, simply run your app from the IDE:

Compose Hot Reload special mode of running application

My application doesn't start in the Hot Reload mode, please help!

Make sure that:

  • you have installed and enabled the Kotlin Toolchain IDEA plugin
  • your module has the jvm target (jvm/app product type, or a library module with jvm platform)
How do I run my app without the Hot Reload mode in the IDE?

Modify the run configuration by removing the "Compose Hot Reload" option:

Compose Hot Reload setting in Run configuration

In this mode, IDEA will recompile and hot-reload your application based on file system changes.

If your JVM application has Compose enabled, you can simply run it from the command line with the kotlin run command.

It is equivalent to running it with the --compose-hot-reload flag:

./kotlin run --compose-hot-reload

If you want to run it without Compose Hot Reload, use the --no-compose-hot-reload flag:

./kotlin run --no-compose-hot-reload

When you run your application with Compose Hot Reload enabled:

  • The Kotlin Toolchain automatically downloads and runs JetBrains Runtime to maximize hot-swap capabilities
  • The Java agent for Compose Hot Reload is attached during execution
  • A small Compose Hot Reload devtools icon appears next to the application window, indicating that the feature is active
  • A file watcher is attached to detect changes in your source files and automatically recompile the application

Compose Hot Reload dev tools

Hot-run specific components

It's also possible to run specific composables in Compose Hot Reload mode instead of your entire application. This is especially useful to check library components or just individual parts of your apps.

To do that, add the Compose Hot Reload runtime API dependency to your module:

dependencies:
  - $compose.hotReload.runtimeApi # for @DevelopmentEntryPoint

And add the @DevelopmentEntryPoint annotation on any parameter-less @Composable function:

@DevelopmentEntryPoint // (1)!
@Composable
fun MySuperComponent() { // (2)!
    MyComponentWithParams(title = "Dummy title", description = "Lorem ipsum dolor sit amet")
}
  1. Makes the component runnable on its own
  2. The component must not have parameters

A clickable gutter icon will appear on the left side of the composable. Compose Hot Reload development entry point usage

Hot-reloading components with parameters

If your component needs parameters, you can create a wrapper component that just creates your component with the parameter values you want to use for the hot run, and annotate the wrapper with @DevelopmentEntryPoint:

@DevelopmentEntryPoint
@Composable
fun MyComponentPreview() {
    MyComponentWithParams(title = "Dummy title", description = "Lorem ipsum dolor sit amet")
}

@Composable
fun MyComponentWithParams(title: String, description: String) {
    // Your composable code here
}

Connect an AI agent

Compose Hot Reload provides an MCP server that lets an AI agent inspect the composables of the running application (including screenshots of them), trigger hot reloads, and interact with the UI by triggering actions such as button clicks, typing text, and scrolling. This is much faster than taking OS screenshots of the desktop app or using mobile simulators.

Start your application with ./kotlin run --compose-hot-reload (or the Run with Compose Hot Reload option in the IDE), and configure your agent to launch the MCP server with the compose-hot-reload-mcp-server command. Most agents read this configuration from an .mcp.json file in the project root:

{
  "mcpServers": {
    "Compose Hot Reload": {
      "command": "./kotlin",
      "args": [
        "compose-hot-reload-mcp-server"
      ]
    }
  }
}

Note

The MCP server requires Compose Hot Reload 1.2.0 or later. It finds the running application via the build directory, so if you customize it, use the same build directory for run --compose-hot-reload.

Custom Compose Hot Reload version

You can optionally customize the version of Compose Hot Reload used by the Kotlin Toolchain this way:

settings:
  compose:
    enabled: true
    experimental:
      hotReload:
        version: 1.0.0-rc3