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:
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.
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.
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.
A lambda function to execute after hot reload. This action should not throw exceptions.
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:
- After changed classes have been reloaded
- After the heap has been migrated
- After static fields have been re-initialized
- 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.
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
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"