Compose HotSwan is a JetBrains IDE plugin plus a Gradle compiler plugin that swaps changed Kotlin classes into a running Compose app on a real device or emulator in under one second, preserving navigation, scroll, and remember state. This skill installs both pieces, wires the canonical Gradle DSL with debugOnly = true, and verifies the first save-to-reload round-trip. Sibling skills cover what does and does not hot-reload (../understanding-hot-reload-limits/SKILL.md), state preservation across reloads, and the AI-driven iteration loop.
Versions verified at the time of authoring. Confirm against the current release notes for newer minors.
When to use this skill
- The developer mentions HotSwan, hot reload, instant UI update, "save and see", "no rebuild", live edit, or fast Compose iteration on device.
- The developer has the HotSwan JetBrains Marketplace plugin installed and asks how to apply the Gradle compiler plugin.
- A team is onboarding a new project to HotSwan and wants the canonical
libs.versions.toml+ root build + app build wiring. - The developer just installed the IDE plugin and the tool window status is stuck before
WATCHING.
When NOT to use this skill
- The question is about JetBrains' own Live Edit or Apply Changes features. HotSwan and those are separate products with different boundaries; do not conflate.
- The question is about Compose Preview rendering inside the IDE. HotSwan targets the running app on a real device or emulator, not preview canvases.
- The question is about which changes hot-reload vs force a rebuild. See
../understanding-hot-reload-limits/SKILL.md. - The question is about preserving complex state across reloads. See the state-preservation sibling skill (out of scope here).
Prerequisites
- Android Studio 2024.3+ or IntelliJ IDEA 2024.3+.
- Kotlin 2.0+ with the
org.jetbrains.kotlin.plugin.composeGradle plugin already applied on the Compose modules. - Gradle 8+.
- A real device or emulator with USB debugging enabled and visible to
adb devices. - The HotSwan IDE plugin installed from JetBrains Marketplace (https://plugins.jetbrains.com/plugin/30551-compose-hotswan/).
Workflow
-
Declare the plugin in
libs.versions.tomlso the version pins centrally:[plugins] hotswan-compiler = { id = "com.github.skydoves.compose.hotswan.compiler", version = "1.2.10" }Pin
1.2.10(latest verified at the time of authoring). Check the HotSwan releases page for newer stable versions before adopting; HotSwan iterates frequently and a newer minor often ships compatibility for a newer Compose runtime. The plugin id iscom.github.skydoves.compose.hotswan.compilerexactly. Do not swap thecom.githubprefix for any other vendor namespace; HotSwan is published undercom.github.skydoves. -
Apply at the root
build.gradle.ktswithapply falseso the plugin is resolved once but only attaches to modules that opt in:plugins { alias(libs.plugins.hotswan.compiler) apply false } -
Apply in each Compose app module's
build.gradle.kts:plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) alias(libs.plugins.hotswan.compiler) } -
Configure the canonical DSL block in the same module file. The capital
SinhotSwanCompileris part of the identifier:hotSwanCompiler { enabled = true debugOnly = true }debugOnly = trueis the default and the rule for production. Release builds MUST NOT carry the HotSwan transformations. -
Sync Gradle. The build should resolve without errors. Confirm the plugin attached by inspecting the build's plugin list (
./gradlew :app:buildEnvironmentor the IDE Gradle tool window). -
Open the HotSwan tool window via
View -> Tool Windows -> HotSwan. ClickStart. The status indicator should change toREADY. -
Run the app normally with the
Runaction. Once the app process connects, the tool window status moves fromREADYtoWATCHING. -
Make a body-only edit and save. Change the color or text inside one composable function body, then save the file. The change should appear on device in under one second; navigation, scroll position, and
remembervalues stay intact. -
If the change does not appear, fall through to the troubleshooting checklist below before changing any Gradle configuration.
Patterns
Pattern: configure with debugOnly true (production safety)
// WRONG
hotSwanCompiler {
enabled = true
}
// WRONG because: omitting `debugOnly = true` allows the HotSwan transformations to attach to release
// builds. They are diagnostic-only and MUST NOT ship in production.
// RIGHT
hotSwanCompiler {
enabled = true
debugOnly = true
}
Pattern: respect the capital S in the DSL block name
// WRONG
hotswanCompiler {
enabled = true
debugOnly = true
}
// WRONG because: the extension is hotSwanCompiler with a capital S in the middle. Lowercase will
// not resolve and Gradle reports an unknown extension.
// RIGHT
hotSwanCompiler {
enabled = true
debugOnly = true
}
Pattern: apply at root with apply false, in modules with alias(...)
// WRONG (root build.gradle.kts)
plugins {
alias(libs.plugins.hotswan.compiler)
}
// WRONG because: applying at the root project triggers the compiler plugin everywhere, including
// modules without Compose. Use apply false at the root and alias(...) inside each Compose module.
// RIGHT (root build.gradle.kts)
plugins {
alias(libs.plugins.hotswan.compiler) apply false
}
// RIGHT (app/build.gradle.kts; Compose module)
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.hotswan.compiler)
}
hotSwanCompiler {
enabled = true
debugOnly = true
}
Pattern: do not depend on HotSwan at the source level
// WRONG
import com.github.skydoves.compose.hotswan.runtime.HotReloadable
@HotReloadable
@Composable
fun Greeting() { Text("Hello") }
// WRONG because: HotSwan is a build-time and IDE-time tool. Production code must not import
// HotSwan runtime classes or annotations; the Gradle plugin attaches transformations during
// compilation without requiring source-level coupling.
// RIGHT
@Composable
fun Greeting() { Text("Hello") }
Pattern: canonical three-file setup (paste-ready)
# gradle/libs.versions.toml
[plugins]
hotswan-compiler = { id = "com.github.skydoves.compose.hotswan.compiler", version = "1.2.10" }
// build.gradle.kts (root)
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.hotswan.compiler) apply false
}
// app/build.gradle.kts
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.hotswan.compiler)
}
hotSwanCompiler {
enabled = true
debugOnly = true
}
Troubleshooting
- Status stuck at
READYafterRun. The device is not connected. Runadb devicesand confirm the device or emulator is listed; reconnect USB or restart the emulator. - Status
WATCHINGbut no reload on save. The save was a structural change (parameter, constructor, interface, inline function, new resource id). Cross-link../understanding-hot-reload-limits/SKILL.mdto identify the boundary. - Reload happens but state is lost. The change cascaded to a tier-2 (composition reset) or tier-3 (
Activity.recreate()) recomposition. Cross-link the state-preservation sibling skill once available. - The
hotSwanCompiler { ... }block does not resolve. Gradle did not sync after adding the plugin. Re-sync; verify Kotlin 2.0+ is the active Kotlin version on the module; verify the alias matches the entry inlibs.versions.toml.
Mandatory rules
- MUST set
debugOnly = true. HotSwan transformations are diagnostic-only and MUST NOT ship in release builds. - MUST apply the Gradle plugin only on Compose modules. Apply at the root project with
apply falseso non-Compose modules are not transformed. - MUST NOT depend on HotSwan classes at the source level. No annotations, no runtime imports. HotSwan is a build-time and IDE-time tool; production code stays pure.
- MUST NOT mix HotSwan with a stale Compose Compiler. Verify
org.jetbrains.kotlin.plugin.composeis applied on Kotlin 2.0+ before adding HotSwan. - MUST pin the HotSwan version in
libs.versions.tomlso all Compose modules in the project use a single version. The latest verified version is1.2.10; check hotswan.dev/docs/releases for newer stable releases before pinning. - PREFERRED: combine HotSwan with the AI iteration loop sibling skill (
../iterating-with-ai-and-mcp/SKILL.md) once the developer is comfortable with manual hot reload and wants Claude to drive the edit-reload-screenshot cycle via MCP.
Verification
-
./gradlew :app:buildEnvironmentlists thecom.github.skydoves.compose.hotswan.compilerplugin among applied plugins. - The HotSwan tool window status reads
WATCHINGafter the app launches withRun. - Editing a
Textcolor inside a composable and saving the file changes the color on device within one second. -
remembervalues, scroll position, and the navigation back stack survive the change. - A release build (
./gradlew :app:assembleRelease) does not include HotSwan transformations. Verify by confirmingdebugOnly = trueis set, or by inspecting the release dex withdexdumpfor the absence of HotSwan-injected helper classes. - No production source file in the module imports a
com.github.skydoves.compose.hotswan.*symbol (grep -R "com.github.skydoves.compose.hotswan" src/main/returns zero hits).
References
- HotSwan JetBrains Marketplace plugin: https://plugins.jetbrains.com/plugin/30551-compose-hotswan/
- HotSwan releases (canonical source for the latest stable version): https://hotswan.dev/docs/releases
- HotSwan documentation portal (skydoves/compose-hotswan-web): https://github.com/skydoves/compose-hotswan-web
- HotSwan Gradle configuration docs: https://github.com/skydoves/compose-hotswan-web (under
/docs/gradle-configuration) - Compose Compiler Gradle plugin (Kotlin 2.0+): https://developer.android.com/jetpack/androidx/releases/compose-compiler
- Sibling skill
../understanding-hot-reload-limits/SKILL.mdfor the boundary between hot-reloadable and rebuild-forcing changes.