Composable-Nametag is a debug tool that overlays the name of every @Composable function as a label on your screen.
Without modifying any existing code, the Kotlin Compiler Plugin (KCP) automatically injects labels at compile time.
See each Composable's name directly on screen, making layout debugging and code review faster.
- Auto injection — The Compiler Plugin injects labels at compile time without touching your existing code.
- Debug only — The compiler plugin and runtime are applied only to
debugbuilds. Release builds contain zero library code — no IR injection, no runtime dependency. - Zero overhead — Works via IR transformation at compile time. In release builds, nothing is injected or included at all.
- Noise filtering — Only PascalCase Composables get labels; lambdas,
remember, property accessors, etc. are ignored. - Customizable skip rules — Exclude composables from labeling via package prefix, name regex, or annotation.
- Build safe — Unsupported Kotlin versions only disable the compiler plugin — the build always succeeds.
Choose one of the following depending on where the library is hosted.
Add mavenCentral() to your plugin repositories in settings.gradle.kts:
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral() // ← required
google()
}
}If using Convention Plugin, also add to build-logic/settings.gradle.kts dependencyResolutionManagement.repositories.
The library is also available via GitHub Packages. Add the repository to settings.gradle.kts:
// settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
google()
maven {
url = uri("https://maven.pkg.github.com/{owner}/{repo}")
credentials {
username = providers.gradleProperty("gpr.user").orNull ?: System.getenv("GITHUB_ACTOR")
password = providers.gradleProperty("gpr.token").orNull ?: System.getenv("GITHUB_TOKEN")
}
}
}
}If using Convention Plugin, also add the same maven { ... } block to build-logic/settings.gradle.kts dependencyResolutionManagement.repositories.
Set credentials in
~/.gradle/gradle.properties:gpr.user=your-github-username gpr.token=ghp_xxxxxxxxxxxxxxxxxxxx # read:packages scope required
Apply the plugin in each Compose module's build.gradle.kts.
It must be declared before the Compose plugin.
// feature/home/build.gradle.kts (Compose module)
plugins {
id("{group-id}") version "{library-version}" // Must be before the Compose plugin
id("org.jetbrains.kotlin.plugin.compose") version "2.1.21"
// ...
}Note
No additional implementation dependency is needed — the plugin adds the runtime library automatically.
The {group-id} is the GROUP configured during publishing (e.g., io.github.dongx0915.composable.nametag for Maven Central).
For projects using a Convention Plugin structure (e.g., build-logic):
Step 1. Add the plugin artifact to your build-logic/convention/build.gradle.kts:
dependencies {
implementation("{group-id}:composable-nametag-gradle:{library-version}")
}Step 2. Apply it inside your Compose Convention Plugin, before the Compose plugin:
// e.g., AndroidComposeConventionPlugin.kt
class AndroidComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("{group-id}") // Must be before the Compose plugin
pluginManager.apply("org.jetbrains.kotlin.plugin.compose")
// ...
}
}
}- Android API 24 (Android 7.0) or higher
- Kotlin 2.1.21 ~ 2.3.20 (see Supported Versions)
- Jetpack Compose (BOM 2025.05.01 or compatible)
- JDK 17+
// Application class or wherever you want to toggle
ComposeDebugConfig.enabled = trueThat's it. All @Composable function names will appear as labels on screen.
| Condition | Behavior |
|---|---|
PascalCase @Composable |
Label shown |
| camelCase (remember, modifier, etc.) | Skipped |
| Lambda / anonymous | Skipped |
| Property accessor | Skipped |
__ prefix |
Skipped |
In addition to the built-in filters, you can exclude additional composables from labeling via the composableNametag { } DSL.
| Property | Type | Description |
|---|---|---|
skipPackages |
List<String> |
Package FQN prefixes. Functions whose file package matches (exact or sub-package) are skipped. |
skipNamePatterns |
List<String> |
Java regex patterns. Functions whose simple name fully matches any pattern are skipped. |
skipAnnotations |
List<String> |
Fully-qualified annotation class names. Functions annotated with any of these are skipped. |
All rules are OR-combined with the built-in rules — if any rule matches, the label is not injected.
// feature/home/build.gradle.kts
composableNametag {
skipPackages.addAll(
"com.myapp.internal",
"com.myapp.designsystem.foundation",
)
skipNamePatterns.addAll(
".*Preview$", // skip `XxxPreview`
"^Themed.*", // skip `ThemedXxx`
)
}For skipAnnotations, pass the fully-qualified name of any annotation defined in your project (e.g. "com.myapp.debug.MyInternalMarker"). Composables marked with that annotation will be skipped.
Configure once in your convention plugin to apply across all Compose modules:
// AndroidComposeConventionPlugin.kt
import com.donglab.compose.debug.gradle.ComposableNametagExtension
pluginManager.apply("{group-id}")
pluginManager.apply("org.jetbrains.kotlin.plugin.compose")
extensions.configure<ComposableNametagExtension> {
skipNamePatterns.add(".*Preview$")
}Individual modules can still add their own rules on top — ListProperty.add/addAll accumulates.
Note
All filtering happens at compile time, so there's zero runtime cost regardless of how many rules you add.
The compiler plugin uses Kotlin IR internal APIs, so it is published per Kotlin version. The Gradle plugin auto-detects your Kotlin version and resolves the matching compiler artifact.
| Kotlin Version | Supported |
|---|---|
| 2.1.21 | ✅ |
| 2.2.0 | ✅ |
| 2.2.10 | ✅ |
| 2.2.20 | ✅ |
| 2.2.21 | ✅ |
| 2.3.0 | ✅ |
| 2.3.10 | ✅ |
| 2.3.20 | ✅ |
- Unsupported versions: Logs a warning once and disables only the compiler plugin. The build proceeds normally.
⚠️ compose-debug-overlay: Kotlin X.Y.Z is not supported.
→ Your build and app are NOT affected.
Composable-Nametag is completely excluded from release builds:
| Debug Build | Release Build | |
|---|---|---|
| Compiler Plugin (IR injection) | Active | Not applied |
| Runtime Library (APK) | Included | Not included |
- The Gradle plugin uses
debugImplementation— the runtime library is not packaged into release APKs. - The compiler plugin's
isApplicablereturnsfalsefor non-debug compilations — no code is injected into release builds. - Result: Release APK contains zero traces of this library. No performance impact, no binary size increase.
- Kotlin 2.1.21 ~ 2.3.20
- AGP 8.6.1
- Compose BOM 2025.05.01
- Gradle 8.7
Apache License 2.0
