Run non-Composable logic after every successful recomposition

credits : https://unsplash.com/
Introduction
- In Jetpack Compose,
SideEffect is a special block you use when you want to do something after your UI has been drawn. It gives us a set of tools to safely handle such side effects, and one of the simplest is calledSideEffect
Why we need SideEffect?
- In Jetpack Compose, UI is built using pure functions — they take input (state) and return UI (Composable functions). This makes Compose powerful and predictable.
- But not everything in an app is pure. Sometimes, we need to interact with things outside the Composable world, like:
- Logging to Logcat
- Reading/writing from disk
- Updating a variable outside the UI and may more…
- These actions are known as side effects because they affect the world outside the Composable function and don’t return a UI element.
- As it gives us a set of tools to safely handle such side effects among them these 4 are the most commonly used by developers:
- LaunchedEffect
- rememberCoroutineScope
- DisposableEffect
- SideEffect
- In this blog, we’ll primarily focus on
SideEffect—why it’s needed and how it works internally. But before diving deep, let’s briefly explore the other three commonly used side-effect handlers in Jetpack Compose:LaunchedEffect,rememberCoroutineScope, andDisposableEffect.
LaunchedEffect and rememberCoroutineScope
- They both are used to handle the execution of suspend functions within a Composable.
- When we use
LaunchedEffect, Compose automatically launches the coroutine when the specified key or state changes. If the key changes again, the existing coroutine is cancelled and a new one is started. rememberCoroutineScopeis used in Jetpack Compose to get a stableCoroutineScope that stays the same across recompositions. This is particularly useful when you want to launch coroutines in response to user interactions, like button clicks or gestures.- Unlike
LaunchedEffect, which is triggered automatically by changes in state or keys,rememberCoroutineScopeis manually controlled — you decide when to launch a coroutine using the scope it provides. The important thing is that this scope is remembered and doesn’t get recreated every time the Composable recomposes. - This means you can safely call
scope.launch { }without worrying about the scope being reset or causing unintended behavior due to recomposition. It helps avoid issues like launching duplicate coroutines or losing coroutine references during UI updates.
DisposableEffect
- Used when you need to set up and clean up something (like a listener, callback, or subscription) based on a key. Think of it as Compose’s version of
onStart()andonStop().
Both
DisposableEffectandSideEffectare used to run non-suspending functions inside Composables.DisposableEffect is designed for setting up and cleaning up resources, such as listeners, and follows the lifecycle of the Composable.
SideEffect
SideEffectis a Composable Side Effect API that lets you execute a block of non-suspending code after every successful recomposition of a Composable.- This means once the UI has been updated and the Composable has finished rendering,
SideEffectgives you a hook to run additional logic — but outside of the actual UI-building process. - Unlike
LaunchedEffect, it doesn’t launch coroutines or deal with suspend functions. Instead, it’s perfect for those small actions that need to happen because of recomposition, but don’t directly affect the UI.
Where we use it?
- 🔍 Logging : You can log whenever a Composable recomposes, which is great for debugging or understanding how often and why recomposition happens.

- 📡 Triggering analytics: When a particular screen or UI state is shown, you might want to fire an analytics event. Since
SideEffectruns after recomposition, it’s safe for such operations.

- 🔄 Synchronizing state with non-Composable code: If you’re interacting with something outside of Compose (like shared preferences, global variables, or a legacy API),
SideEffectensures that you update them in sync with the latest Composable state — and without breaking Compose’s unidirectional data flow.

- Every time ProfileScreen recomposes (e.g., userName changes), SideEffect is called. It writes the updated name to SharedPreferences safely after the composition is done. This avoids side effects during UI rendering and keeps your state in sync with non-Compose storage
SideEffectruns after every successful recomposition and is useful for tasks like logging or triggering analytics.
What Happens Internally When You Use SideEffect?
- Now, let’s explore the internal working of
SideEffectin Jetpack Compose and see what happens when we call this block of code.

- When
ProfileScreen(name = "Richa")is called inside a Compose hierarchy (e.g., insidesetContent {}), the Compose runtime begins composition or recomposition. - Compose begins executing the function top to bottom and hit SideEffect{…}. The
Log.d(...)will not run immediately - Inside the Compose runtime,
SideEffectlooks something like this:

- The
effectlambda (Log.d(...)) is stored in a side-effect buffer and Compose doesn’t execute it yet — it just registers it for later. - Then Compose proceeds to execute Text(“Hello $name!”) This part builds a node in the UI tree with the value
"Hello Richa".After building or diffing the Composable UI tree this text appears on Screen. - Now comes the post-composition phase. Compose executes all recorded side effects, including the one you defined. Now “SideEffect: Recomposed with name: Richa” now your log gets printed.
It runs only after the Composable has successfully recomposed, ensuring your logs always capture the latest and most accurate UI state.
- This is guaranteed to happen only after recomposition was successful, so your logs will always reflect the most accurate UI state.
- Internally, Compose records the lambda using
recordSideEffect(...). Once the recomposition successfully completes, the effect is executed exactly once per recomposition cycle.
Real UseCase Sample

- This allows you to track how the screen state changes with every recomposition — a simple and powerful debugging technique.
Job Offers
Summary
- We must be careful when introducing operations that interact with the outside world — like logging, analytics, or state syncing in Compose.
- That’s where
SideEffectshines it lets you safely perform side effects like logging after every successful recomposition. - Think of Compose as collecting side effects during UI composition and then executing them in a batch after the composition is finished.
Understanding how
SideEffectworks helps you write cleaner, more predictable Compose code—and debug UI behavior more effectively.
- In the next blog, we’ll dive deep into the internal workings of
LazyColumn andLazyRow in Jetpack Compose — exploring how they manage item rendering, recycling, and scroll state.
We’ll also compare them with traditional Android views likeRecyclerViewto understand the performance differences and architectural improvements Compose brings. - Reference Link : https://developer.android.com/develop/ui/compose/side-effects
Till then if you found this helpful:
💬 Leave a comment if you’ve used SideEffect in a unique or interesting way.
👏 Clap if you learned something new about how Compose handles recomposition and side effects and share this blog with your fellow Android developers to spread the Compose knowledge!
Stay tuned — it’s going to be a fun ride into Compose internals!🙌
Take Care 🙂
This article was previously published on proandroiddev.com.



