Skip to main content
The Compose Hot Reload Runtime API provides a set of functions and properties that allow you to interact with the hot reload system at runtime. This is useful for managing global state, performing custom cleanup, or implementing development-only features.

Installation

Add the runtime API dependency to your project:
build.gradle.kts
implementation("org.jetbrains.compose.hot-reload:hot-reload-runtime-api:<version>")

Core APIs

isHotReloadActive

A boolean property that indicates whether the application was started with Hot Reload enabled.
isHotReloadActive
Boolean
Returns true if the application is running with Hot Reload, false otherwise.
Usage:
import org.jetbrains.compose.reload.isHotReloadActive

fun main() {
    if (isHotReloadActive) {
        println("Running with Hot Reload")
        // Enable development features
    } else {
        println("Running in production mode")
    }
}
This property is commonly used to conditionally enable development-only features or to gate hot reload-specific code:
if (isHotReloadActive) {
    // Register cleanup hooks or development tools
    setupHotReloadHooks()
}

staticHotReloadScope

A static scope for registering hooks that execute after a hot reload completes.
staticHotReloadScope
HotReloadScope
Provides access to the global HotReloadScope instance for registering reload hooks.
Important: When using staticHotReloadScope, you must manually manage the lifecycle of registered hooks. Failing to dispose of hooks can lead to memory leaks. For most use cases, prefer using AfterHotReloadEffect instead.

invokeAfterHotReload()

Registers an action to be executed after a hot reload completes.
action
() -> Unit
required
A lambda function to execute after hot reload. This action should not throw exceptions.
return
AutoCloseable
An AutoCloseable that can be used to unregister the hook. Call close() to remove the hook when it’s no longer needed.
Execution Timing: The action is executed:
  1. After changed classes have been reloaded
  2. After the heap has been migrated
  3. After static fields have been re-initialized
  4. Before the next frame is rendered
Usage:
import org.jetbrains.compose.reload.staticHotReloadScope
import org.jetbrains.compose.reload.DelicateHotReloadApi

@OptIn(DelicateHotReloadApi::class)
fun setupGlobalStateReset() {
    val registration = staticHotReloadScope.invokeAfterHotReload {
        // Reset global state
        globalCache.clear()
        reinitializeServices()
    }
    
    // Later, when cleanup is needed:
    // registration.close()
}
Example: Resetting ViewModels:
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import org.jetbrains.compose.reload.staticHotReloadScope
import org.jetbrains.compose.reload.DelicateHotReloadApi

@OptIn(DelicateHotReloadApi::class)
fun scheduleViewModelReset() {
    val viewModelStoreOwner = LocalViewModelStoreOwner.current
    staticHotReloadScope.invokeAfterHotReload {
        viewModelStoreOwner?.viewModelStore?.clear()
    }
}

AfterHotReloadEffect

A Composable function that registers an effect to execute after a hot reload. This is the recommended way to respond to hot reload events within Compose code.
action
() -> Unit
required
A lambda function to execute after each successful hot reload.
Advantages over staticHotReloadScope:
  • Automatic lifecycle management tied to the Composable’s lifecycle
  • No need to manually dispose of hooks
  • Integrates naturally with Compose’s effect system
Usage:
import androidx.compose.runtime.*
import org.jetbrains.compose.reload.AfterHotReloadEffect
import org.jetbrains.compose.reload.DelicateHotReloadApi

@OptIn(DelicateHotReloadApi::class)
@Composable
fun MyApp() {
    var reloadCount by remember { mutableStateOf(0) }
    
    AfterHotReloadEffect {
        reloadCount++
        println("Hot reload completed! Count: $reloadCount")
    }
    
    Text("Reloaded $reloadCount times")
}
Example: Refreshing Data After Reload:
@OptIn(DelicateHotReloadApi::class)
@Composable
fun DataScreen(viewModel: DataViewModel) {
    AfterHotReloadEffect {
        // Refresh data after code changes
        viewModel.refreshData()
    }
    
    // Rest of your UI
}
Example: Resetting ViewModels (Composable):
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import org.jetbrains.compose.reload.AfterHotReloadEffect
import org.jetbrains.compose.reload.DelicateHotReloadApi

@OptIn(DelicateHotReloadApi::class)
@Composable
fun App() {
    val viewModelStoreOwner = LocalViewModelStoreOwner.current
    
    AfterHotReloadEffect {
        viewModelStoreOwner?.viewModelStore?.clear()
    }
    
    // Rest of your app
}

DevelopmentEntryPoint (Deprecated)

Provides an entry point for Compose Hot Reload to reload code.
This composable is deprecated and no longer needed. When using a regular Window, there is no need to wrap your code manually. This function is only applicable for non-window-based applications and will be removed in a future version.
child
@Composable () -> Unit
required
The composable content to wrap.
Usage (Legacy):
import org.jetbrains.compose.reload.DevelopmentEntryPoint
import org.jetbrains.compose.reload.DelicateHotReloadApi

@OptIn(DelicateHotReloadApi::class)
@Composable
fun main() = DevelopmentEntryPoint {
    // Your app content
    MyApp()
}
For modern applications using Window, you can simply use:
fun main() = application {
    Window(onCloseRequest = ::exitApplication) {
        MyApp()
    }
}

Best Practices

Use AfterHotReloadEffect for Compose Code

When working within Composable functions, prefer AfterHotReloadEffect over staticHotReloadScope:
// Good
@Composable
fun MyScreen() {
    AfterHotReloadEffect {
        resetState()
    }
}

// Avoid - requires manual cleanup
fun setupHooks() {
    staticHotReloadScope.invokeAfterHotReload {
        resetState()
    }
}

Gate Development-Only Code

Use isHotReloadActive to prevent development code from running in production:
if (isHotReloadActive) {
    AfterHotReloadEffect {
        logDebugInfo()
    }
}

Handle Exceptions Carefully

Actions registered with hot reload hooks should not throw exceptions. If an exception occurs, it will be re-thrown at the end of the main thread’s dispatch queue:
AfterHotReloadEffect {
    try {
        potentiallyFailingOperation()
    } catch (e: Exception) {
        logger.error("Hot reload hook failed", e)
    }
}

Clean Up Properly

When using staticHotReloadScope, always clean up your registrations:
class MyService : AutoCloseable {
    private val registration = staticHotReloadScope.invokeAfterHotReload {
        reset()
    }
    
    override fun close() {
        registration.close()
    }
}

Implementation Details

Platform Support

The Runtime API is available on all platforms supported by Compose Hot Reload. On platforms where hot reload is not active, the APIs provide no-op implementations:
  • isHotReloadActive returns false
  • AfterHotReloadEffect does nothing
  • staticHotReloadScope.invokeAfterHotReload() returns an empty AutoCloseable

System Property

The isHotReloadActive property reads from the system property compose.reload.isActive, which is set to "true" when the application launches with hot reload enabled:
public actual val isHotReloadActive: Boolean =
    System.getProperty("compose.reload.isActive") == "true"

Build docs developers (and LLMs) love