Library / SDK
theapache64/rebugger avatar
theapache64/rebugger

Rebugger: a Compose utility that names the argument that caused the recomposition

A simple Compose Multiplatform library designed to print the reason for recomposition in your console/Logcat window."

1,047 stars10 forksKotlinApache-2.0

At a glance

What is it?
Rebugger is a single composable you drop into a screen with a map of the values you care about, and it prints in Logcat which of them changed and why the recomposition happened, with an IntelliJ plugin to generate the call.
Who is it for?
Rebugger fits a Compose developer who has a screen that recomposes and cannot work out which input caused it, and who wants a one-call answer in Logcat rather than a full tracing tool. It is a poor fit for a production build, since it is a debugging aid you have to remove before shipping, and a poor fit if you need to know why a recomposition happened deep inside a third-party composable rather than in your own screen.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 48 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap Rebugger fills

Rebugger is a recomposition debugger, and the problem it addresses is stated in the first line of the README: being a compose dev, the enemy number one is unnecessary recompositions.

The tooling people reach for today only counts. The README names three: a recomposition highlighter, a LogComposition helper, and the layout inspector. All three can tell you that a recomposition happened and how many there were. None of them tells you why.

Rebugger is a simple Compose utility function that can track the change in the given arguments, and it prints the reason for recomposition in your Logcat window. The difference is that you hand it a map of the values you care about, and it compares them across recompositions, so the output names the specific argument that changed rather than counting a recomposition.

The argument can be a function argument or a state. That is the design choice that makes it useful, because the two common causes of a surprising recomposition are a function parameter that is a new object each time and a remembered state that something else wrote to, and the two are tracked the same way.

The project is Apache-2.0 licensed, the primary language is Kotlin, the last push was on 2026-09-05, and the releases are 1.0.1 and 1.0.0 both from 2025-08-24, after 1.0.0-rc03 from 2024-04-03. So the library reached 1.0 in 2025 and has been stable since.

The repository layout confirms the multiplatform ambition. There is a rebugger/ directory for the library, a composeApp/ directory and an iosApp/ directory, build.gradle.kts and settings.gradle.kts, gradle.properties, local.properties, and a README_CMP.MD which is the Compose Multiplatform version of the README. The two app directories are the sample projects, one for the JVM and Android side and one for iOS, and the presence of an iOS app directory is what makes the library Compose Multiplatform rather than Android-only.

Adding it, and what the call looks like

The dependency is one line in your Gradle build, in either Kotlin script or Groovy form:

kotlin
repositories {
    ...
    mavenCentral()
}

dependencies {
    // [latest version - i promise!]
    implementation("io.github.theapache64:rebugger:1.0.1")
}

Then you call Rebugger inside the composable you want to watch, passing the states or args you want to track:

kotlin
@Composable
fun VehicleUi(
    car: Car,
    bike: Bike,
) {
    var human by remember { mutableStateOf(Human("John")) }

    // Call Rebugger and pass the states you want to track. 
    // It could be a function arg or a state
    Rebugger(
        trackMap = mapOf(
            "car" to car,
            "bike" to bike,
            "human" to human
        ),
    )

The map keys are the labels that appear in the output, and the values are what gets compared. Passing the same map shape every time is what makes the comparison meaningful, because a key that disappears or changes name would be reported as a change.

Then you look at Logcat and search for Rebugger. The sample outputs in the README show what you get: the line printed when Rebugger is hooked into the composable, and then separate cases for the composable recomposing because the car instance changed, because both the car and the bike instance changed, and because the human instance changed, which is the state inside the composable. Those three cases are the whole point of the tool, because they are the three shapes a Compose developer needs to distinguish.

The keys are named after the arguments, so the output reads as an explanation of the recomposition rather than a diff of two objects.

Auto name picking, and how to override it

There is one documented limitation, and the README is precise about it.

The issue is auto name picking. When Rebugger is placed deep inside the composable, it may not be able to pick the correct composable name. The example given is a Rebugger placed inside a Button content lambda:

kotlin
Button(
    onClick = {
        //...
    }
) {

    // 🟠 Inside Button's content lambda
    Rebugger(
        trackMap = mapOf(
            "car" to car,
            "bike" to bike,
            "human" to human
        ),
    )
}

The name is derived from the call stack, and a content lambda is not a composable frame the tool can name, so the output names the wrong thing.

The fix is to pass composableName explicitly:

kotlin
Rebugger(
    composableName = "Button's body",
    trackMap = mapOf(
        "car" to car,
        "bike" to bike,
        "human" to human
    ),
)

So the failure mode is a wrong label rather than a wrong answer. The recomposition detection itself still works, because that comes from comparing the tracked values, but the output would tell you that a composable called something other than the one you are looking at recomposed, which is precisely the moment when you need the tool to be right.

The practical rule is to put the Rebugger call directly in the composable whose recomposition you are investigating, and to pass composableName whenever the call sits inside a lambda.

Redirecting the output, and the IntelliJ plugin

The output goes to Logcat under a default tag, and the tag and the logger are both configurable through RebuggerConfig.init. The example shows the intended use, which is routing the messages into an existing logging setup:

kotlin
class App : Application() {
    // ...
    
    override fun onCreate() {
        super.onCreate()
        
        // ...
        
        RebuggerConfig.init(
            tag = "MyAppRebugger", // changing default tag
            logger = { tag, message -> Timber.i(tag, message) } // use Timber for logging
        )
    }
}

The logger is a function taking a tag and a message, so anything that can log can be plugged in. Timber is the example because it is the common Android logging library, and pointing Rebugger at Timber means the recomposition reasons end up in the same log stream as everything else, which is where you would look anyway when investigating a performance problem.

There is also a Rebugger IntelliJ plugin, published on the JetBrains marketplace, that generates the Rebugger function call for you. The value of that is the same as the value of the tool itself: the call has a particular shape, with a trackMap whose keys must match the argument names, and generating it saves you from both the typing and the chance of a key that does not match.

Together, the plugin and the config hook are what make the tool a normal part of a debug session rather than a one-off snippet. You drop the call in, it prints the reason, and when you are done you remove it.

The repository also carries a .idea/ directory, so the IDE configuration is checked in, and the build is a Gradle Kotlin DSL project with the wrapper included, both gradlew and gradlew.bat.

Reading the Logcat output, and the removal problem

The sample outputs section is the part of the documentation that explains how to read the result, and it walks through four situations.

The first is the line printed when Rebugger is hooked into the composable, which is the setup confirmation and tells you the tag and the name of the composable being watched. The next three are the cases that matter: the composable recomposing because the car instance changed, because both the car and the bike instance changed, and because the human instance changed, which is the state declared inside the composable with remember and mutableStateOf.

Those cases are chosen well, because they separate the three usual suspects. A new object identity on a function argument is the first, and the classic cause is a data class without equals, so a comparison that should be equal reads as changed. The second case shows the tool handling several tracked values changing at once, which is what tells you the recomposition was caused by a shared dependency rather than by one of the inputs. The third is state written from inside the composable, which is the case where nothing in the call arguments changed and you would otherwise have no idea what triggered it.

The practical workflow is short. Add the dependency, drop the call into the composable, trigger the recomposition, read the Logcat, and remove the call. That last step is the one that matters for a production build, and there is no mention of a release-mode guard or a debug-only variant of the call in the documentation, so the removal is manual. A debugging aid that prints on every recomposition is noise in a release build, and a library that leaves that to you is a library you will find yourself grepping for before a release.

The IntelliJ plugin and the configurable logger exist to make the tool pleasant enough that using it is worth the cleanup afterwards.

Editorial conclusion

Rebugger fits a Compose developer who has a screen that recomposes and cannot work out which input caused it, and who wants a one-call answer in Logcat rather than a full tracing tool. It is a poor fit for a production build, since it is a debugging aid you have to remove before shipping, and a poor fit if you need to know why a recomposition happened deep inside a third-party composable rather than in your own screen. Before adding it, check the auto name picking behaviour, because a Rebugger placed inside a Button content lambda will report the wrong composable name unless you pass composableName explicitly.

Frequently asked questions

What is Rebugger and what problem does it solve?

Rebugger is a Compose Multiplatform utility function that tracks the change in the arguments you give it and prints the reason for a recomposition in your Logcat window. Existing tools like a recomposition highlighter, LogComposition and the layout inspector can count recompositions, but there was no direct way to understand why one happened.

How do I add Rebugger to a project?

Add the Gradle dependency implementation("io.github.theapache64:rebugger:1.0.1") from mavenCentral, then call Rebugger inside the composable with a trackMap of the arguments or states you want to watch. Then search Logcat for Rebugger.

What is the auto name picking limitation in Rebugger?

When Rebugger is placed deep inside a composable, for example inside a Button content lambda, it may not be able to pick the correct composable name and will print a different one. The fix is to pass the composableName argument explicitly, for example composableName = "Button's body".

Can I send Rebugger output to a different logger?

Yes. RebuggerConfig.init accepts a tag and a logger, and the logger is a function taking a tag and a message, so you can route the output to Timber or any other logging setup from your Application class.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. theapache64/rebugger on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/theapache64-rebugger.svg)](https://hysenlabs.com/projects/theapache64-rebugger)

Was this useful?