Skip to main content

Overview

Compose Hot Reload enables you to see code changes instantly in your running Compose Multiplatform application without restarting. This page explains the architecture, components, and the flow of how code changes are detected and applied.

Architecture

Compose Hot Reload consists of four main components that work together to enable hot reloading:

1. Hot Reload Agent

The Hot Reload Agent is a Java agent that runs inside your application’s JVM process. It is loaded via the -javaagent JVM argument. Key responsibilities:
  • Class Redefinition: Uses the JVM’s enhanced class redefinition capabilities (DCEVM) to reload classes at runtime
  • Instrumentation: Transforms classes to support hot reload features like static field reinitialization
  • Orchestration Server/Client: Manages communication with other components
  • Window Tracking: Instruments window creation to enable dev tools window snapping
  • Compose Integration: Tracks Compose runtime state and triggers recomposition after reload
The agent is initialized in the premain method and starts these subsystems:
fun premain(args: String?, instrumentation: Instrumentation) {
    startDevTools()
    startOrchestration()
    createPidfile()
    startWritingLogs()
    
    launchWindowInstrumentation(instrumentation)
    launchComposeInstrumentation(instrumentation)
    launchRuntimeTracking(instrumentation)
    launchReloadRequestHandler(instrumentation)
    launchJdwpTracker(instrumentation)
}
Location: hot-reload-agent/src/main/kotlin/org/jetbrains/compose/reload/agent/

2. Orchestration Protocol

The orchestration protocol is a lightweight broadcast communication system that allows all components to send and receive messages. Key features:
  • Connection Handshake: Uses magic number 24111602 for protocol validation
  • Message Framing: Each message has a type, size, and binary payload
  • Serialization: Uses Java serialization or custom encoders (protocol v1.4+)
  • Acknowledgments: Server acknowledges each received message
Message Flow:
Client                                    Server
──────                                    ──────
     ───────────► Magic Number ───────────►
     ───────────► Protocol Version ────────►
     ◄──────────── Magic Number ────────────
     ◄──────────── Protocol Version ─────────
     ───────────► Introduction ────────────►
     ◄──────────── ClientConnected ──────────
The orchestration can run in two modes:
  • Server Mode: The agent hosts the orchestration server (default)
  • Client Mode: The agent connects to an external orchestration server (e.g., when dev tools is available)
Location: hot-reload-orchestration/

3. Dev Tools

Dev Tools is a separate process that provides the developer interface and manages the build system integration. Key responsibilities:
  • UI Overlay: Shows reload status, logs, and provides the “Reload UI” button
  • Recompiler Management: Triggers Gradle builds in response to reload requests
  • Error Reporting: Displays compilation errors and runtime issues
  • Window Management: Optionally snaps to the application window
Modes:
  • Normal: Window attached to application window
  • Detached (-Dcompose.reload.devToolsDetached=true): Standalone window
  • Headless (-Dcompose.reload.devToolsHeadless=true): No UI, background only
  • Disabled (-Dcompose.reload.devToolsEnabled=false): No dev tools process
The dev tools process starts a recompiler that listens for RecompileRequest messages:
internal fun launchRecompiler(): Future<Unit> = launchTask("Recompiler", recompilerThread.dispatcher) {
    val recompiler = ServiceLoader.load(RecompilerExtension::class.java)
        .firstNotNullOfOrNull { extension -> extension.createRecompiler() }
    
    messages.filterIsInstance<RecompileRequest>().collect { request ->
        val exitCode = recompiler.buildAndReload(context)
        RecompileResult(request.messageId, exitCode.code).send()
    }
}
Location: hot-reload-devtools/src/main/kotlin/org/jetbrains/compose/devtools/

4. Gradle Plugin

The Gradle plugin integrates hot reload into your build configuration. Key responsibilities:
  • Task Creation: Registers hotRunJvm, hotRunAsync, and reload tasks
  • Classpath Configuration: Sets up agent and dev tools dependencies
  • JBR Provisioning: Ensures application runs with JetBrains Runtime
  • Argument Injection: Configures JVM arguments for hot reload
The plugin creates run tasks that launch your application with the hot reload agent:
tasks.register<ComposeHotRun>("hotRunJvm") {
    mainClass.set("com.example.MainKt")
    
    withComposeHotReload {
        setAgentJar(composeHotReloadAgentJar)
        setDevToolsEnabled(provider { true })
        setHotClasspath(compilationClasspath)
        // ... more configuration
    }
}
Location: hot-reload-gradle-plugin/src/main/kotlin/org/jetbrains/compose/reload/gradle/

How Code Changes Are Applied

When you save changes to your code, here’s what happens:

Step 1: Change Detection

The detection mechanism depends on the reload mode: Explicit Mode:
  • You manually trigger reload via IDE button or ./gradlew reload
  • Gradle file watching is not enabled
Auto Mode:
  • Gradle’s continuous build (--watch-fs) detects file changes
  • Automatically triggers recompilation

Step 2: Recompilation

  1. A RecompileRequest message is sent via orchestration
  2. Dev tools receives the request and invokes the Gradle recompiler
  3. Gradle incrementally compiles only changed files
  4. Compilation produces updated .class files
  5. Dev tools sends a RecompileResult message back

Step 3: Class Analysis

The agent analyzes the changed class files:
fun Context.reload(
    instrumentation: Instrumentation,
    reloadRequestId: OrchestrationMessageId,
    pendingChanges: Map<File, ReloadClassesRequest.ChangeType>
): Try<Reload> = Try {
    val definitions = pendingChanges.mapNotNull { (file, change) ->
        if (!file.isClass()) return@mapNotNull null
        
        val code = file.readBytes()
        val classId = ClassId(code)
        val loader = findClassLoader(classId).get()
        val originalClass = loader.loadClass(classId.toFqn())
        
        // Transform for statics initialization
        val transformed = clazz.transformForStaticsInitialization(originalClass)
        
        ClassDefinition(originalClass, transformed)
    }
    
    // ...
}
Key operations:
  • Extract class metadata (name, package, loader)
  • Find the ClassLoader that loaded the original class
  • Apply transformations (e.g., static field reinitialization)
  • Create ClassDefinition objects for redefinition

Step 4: Class Redefinition

The agent uses the JVM’s enhanced class redefinition:
instrumentation.redefineClasses(*definitions.toTypedArray())
This requires the -XX:+AllowEnhancedClassRedefinition flag, which is only available in JetBrains Runtime (JBR). Standard JDK does not support this level of class redefinition.

Step 5: State Invalidation

After classes are redefined:
  1. Static Reinitialization: Static fields are reinitialized based on the configured mode
  2. Dirty Scope Resolution: Analyze which Compose functions are affected
  3. Compose Invalidation: Mark affected @Composable functions as invalid
  4. Resource Cache Clearing: Clear any resource caches if needed

Step 6: Recomposition

Finally, Compose is notified to recompose:
  1. The agent tracks all active Compose runtimes
  2. For each runtime, it invalidates the dirty scopes
  3. Compose schedules recomposition on the UI thread
  4. Your UI updates with the new code

The Role of JetBrains Runtime

JetBrains Runtime (JBR) is required for Compose Hot Reload because it includes enhanced class redefinition capabilities: Standard JDK limitations:
  • Can only redefine method bodies
  • Cannot add/remove methods or fields
  • Cannot change class hierarchy
JetBrains Runtime (DCEVM):
  • Can add/remove methods and fields
  • Can change method signatures
  • Can modify class hierarchy (with limitations)
  • Enables true hot code replacement
The enhanced redefinition is enabled via:
-XX:+AllowEnhancedClassRedefinition
This flag is automatically added by the Gradle plugin when launching hot reload tasks.
Learn more about obtaining and configuring JetBrains Runtime in the JetBrains Runtime guide.

Classpath and Dependencies

The hot reload system uses a special “hot classpath” to track which classes can be hot reloaded:
-Dcompose.reload.hotApplicationClasspath=/path/to/classes:...
Classes on the hot classpath:
  • Are monitored for changes
  • Can be hot reloaded
  • Have their dependencies tracked
Classes not on the hot classpath (e.g., libraries from Maven):
  • Cannot be hot reloaded
  • Require application restart if changed

Configuration Properties

The system is configured via system properties:
PropertyPurpose
compose.reload.isHotReloadActiveIndicates hot reload is active
compose.reload.mainClassThe main class being executed
compose.reload.pidFileProcess ID file for orchestration
compose.reload.orchestration.portOrchestration server port
compose.reload.devToolsEnabledEnable/disable dev tools
compose.reload.buildSystemBuild system type (Gradle, Amper)
compose.reload.virtualMethodResolveEnabledTrack interface implementations
compose.reload.dirtyResolveDepthLimitDependency traversal depth
These properties are automatically set by the Gradle plugin.

Debugging Hot Reload

To debug the hot reload system: Enable debug logging:
./gradlew hotRunJvm -Dcompose.reload.logLevel=DEBUG
Check agent logs: Logs are written to the dev tools console and can also be redirected:
-Dcompose.reload.logStdout=true
Inspect orchestration messages: All orchestration messages are logged at DEBUG level, showing the communication flow between components.

Summary

Compose Hot Reload is a sophisticated system that combines:
  • A Java agent for class redefinition and instrumentation
  • An orchestration protocol for component communication
  • A dev tools process for build management and UI
  • A Gradle plugin for seamless integration
  • JetBrains Runtime for enhanced class redefinition
Together, these components enable near-instantaneous code updates without restarting your application, dramatically improving the development experience for Compose Multiplatform applications.

Build docs developers (and LLMs) love