Copy as Markdown[Open in ChatGPT](https://chatgpt.com/?q=Read%20https%3A%2F%2Fcoralogix.com%2Fdocs%2Fuser-guides%2Frum%2Fsdk-installation%2Fandroid%2Fjetpack-compose.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fcoralogix.com%2Fdocs%2Fuser-guides%2Frum%2Fsdk-installation%2Fandroid%2Fjetpack-compose.md%20and%20help%20me%20with%20my%20question%20about%20this%20Coralogix%20documentation%20page.)

# Coralogix Android SDK — Compose

Jetpack Compose support for the [Coralogix Android RUM SDK](https://coralogix.com/docs/user-guides/rum/sdk-installation/android.md).

Adds auto-initialization of Compose instrumentation, navigation tracking, interaction naming, and session replay masking for Compose UIs.

## Requirements[​](#requirements "Direct link to Requirements")

* Coralogix Android RUM SDK `2.16.0+`
* Jetpack Compose

## Installation[​](#installation "Direct link to Installation")

Add the dependency to your app's Gradle build file:

```
dependencies {

    implementation("com.coralogix:android-sdk-compose:2.21.1")

}
```

Note

The core SDK (`com.coralogix:coralogix-android-sdk`) must also be initialized before the Compose module has any effect. See the [core SDK README](https://coralogix.com/docs/user-guides/rum/sdk-installation/android.md) for initialization instructions.

## Setup[​](#setup "Direct link to Setup")

No extra initialization code is required. The Compose module auto-initializes via **AndroidX Startup** when the artifact is present on the classpath. Once the core SDK is initialized in your `Application.onCreate()`, Compose support is active automatically.

## API Reference[​](#api-reference "Direct link to API Reference")

### `NavigationTrackingEffect`[​](#navigationtrackingeffect "Direct link to navigationtrackingeffect")

Tracks Compose Navigation route changes and reports them to Coralogix as screen navigation events.

```
@Composable

fun MyApp() {

    val navController = rememberNavController()



    NavigationTrackingEffect(navController = navController)



    NavHost(navController = navController, startDestination = "home") {

        composable("home") { HomeScreen() }

        composable("detail/{id}") { DetailScreen() }

    }

}
```

Call `NavigationTrackingEffect` once at the top level of your composable tree, passing your `NavController`. Each route change is reported as a navigation event in the active session.

### `Modifier.coralogixName(name: String)`[​](#modifiercoralogixnamename-string "Direct link to modifiercoralogixnamename-string")

Sets the element name reported in Coralogix user interaction spans for a composable.

By default, interactable composables are reported as `"Composable"`. Use this modifier to assign a meaningful name for dashboards and session replays.

```
Button(

    onClick = { addToCart() },

    modifier = Modifier.coralogixName("AddToCartButton")

) {

    Text("Add to Cart")

}
```

## Automatic Interaction Tracking[​](#automatic-interaction-tracking "Direct link to Automatic Interaction Tracking")

Once the Compose module is on the classpath, tap and scroll interactions on Compose elements are captured automatically — no additional code is required.

**Taps:** When a user taps an interactable composable (a `Button`, a composable with `Modifier.clickable`, etc.), the SDK walks the semantics tree to find the tapped node and records an interaction span. The span's element name is the node's `contentDescription`, or `"Composable"` by default — use `Modifier.coralogixName()` to assign a meaningful name.

**Scrolls:** Scrollable containers (`LazyColumn`, `LazyRow`, `ScrollableColumn`, etc.) that expose scroll semantics are tracked. Scroll spans are attributed to the container node.

### Limitations[​](#limitations "Direct link to Limitations")

* Only composables that expose semantics to the accessibility system are tracked. Custom composables that draw content without setting semantics properties will not be hit-tested.
* Elements marked with `Modifier.semantics { invisibleToUser() }` are pruned from the semantics tree before the SDK sees them. Taps on these elements will not produce interaction spans (see [Known Limitations](#modifier-semantics-invisibletouser----elements-invisible-to-the-sdk)).

### `Modifier.coralogixMasked()`[​](#modifiercoralogixmasked "Direct link to modifiercoralogixmasked")

Opts a composable into Coralogix masking, regardless of the global masking policy.

```
Text(

    text = user.creditCardNumber,

    modifier = Modifier.coralogixMasked()

)
```

Masking applies to the whole subtree — every descendant is masked too, and a descendant cannot be opted back out. Applying the modifier to a container is therefore enough to cover its contents, which is the recommended way to protect a composite element such as a PIN keypad whose keys are individually clickable.

A masked composable, and anything inside it:

* is covered by a black overlay in session replays;
* absorbs tap markers — a tap landing anywhere inside it draws no marker in the recording, so the replay does not reveal which part of the masked area was touched;
* reports its inner text as `***` in user interaction events.

Everything else on the interaction event is reported as-is — including the element's test tag, its `Modifier.coralogixName`, and the touch coordinates — and the event is still emitted. This matches the Coralogix web SDK: masking hides what an element says, not what it is or that it was used. The event carries `interaction_context.is_masked_element` (always present) so you can drop or rewrite masked interactions in `beforeSend` — see the core README's [Privacy and masking](https://coralogix.com/docs/user-guides/rum/sdk-installation/android.md#privacy-and-masking) section for recipes.

> **Upgrading from 2.19.4:** that release also redacted the id, name, and resolved element of a masked composable. From 2.20.0 those fields are reported as-is again to match the web SDK; a `coralogixName` you would not want reported for a masked element should be handled via `is_masked_element` in `beforeSend`.

## Session Replay Masking[​](#session-replay-masking "Direct link to Session Replay Masking")

The Compose module integrates with the core SDK's privacy masking pipeline. All masking rules configured in `SessionReplayOptions` apply to Compose content, with the limitations noted below.

### Text masking[​](#text-masking "Direct link to Text masking")

`maskAllTexts = true` and `textsToMask` work for Compose nodes whose `SemanticsProperties.Text` is non-empty (i.e. composables that expose text to the accessibility system — `Text`, `Button` labels, etc.).

```
SessionReplayOptions(

    maskAllTexts = false,

    textsToMask = listOf("(?i)password", "tok_.*")   // Kotlin regex, not JS /pattern/flags syntax

)
```

Note

Regex patterns use Kotlin/Java syntax. Write `(?i)button` for case-insensitive matching, **not** `/button/i`.

### Explicit masking[​](#explicit-masking "Direct link to Explicit masking")

`Modifier.coralogixMasked()` masks the composable and its whole subtree unconditionally, regardless of any global policy. See [`Modifier.coralogixMasked()`](#modifiercoralogixmasked) for exactly what masking covers.

> **Limitation:** a real Android View embedded via `AndroidView { }` inside a masked composable is still covered by the black overlay, but is not treated as masked for interaction reporting, so its text is reported normally. Embedded views are attached at the root of the Compose host rather than under the composable that contains them, so the masked ancestor is not visible to them. Wrapping in another masked composable does not help. If the embedded view's text is sensitive, mask it directly inside the factory:
>
> ```
> AndroidView(factory = { ctx -> MySensitiveView(ctx).apply { maskView() } })
> ```

### Image masking[​](#image-masking "Direct link to Image masking")

`maskAllImages = true` masks composables that expose `Role.Image` semantics.

```
SessionReplayOptions(maskAllImages = true)
```

> **Limitation:** `Role.Image` is only set when `contentDescription != null` on `Image` / `AsyncImage` composables. Images without a content description are not detected and will **not** be masked by this option. Use `Modifier.coralogixMasked()` on the composable directly to guarantee masking in those cases.

### Password field masking[​](#password-field-masking "Direct link to Password field masking")

`maskInputFieldsOfTypes = listOf(EditTextType.PASSWORD)` masks Compose text fields that use `PasswordVisualTransformation`.

```
SessionReplayOptions(

    maskInputFieldsOfTypes = listOf(EditTextType.PASSWORD)

)
```

> **Limitation:** For Compose nodes, only `EditTextType.PASSWORD` is evaluated — detected via `SemanticsProperties.Password`, which is set by `PasswordVisualTransformation`. All other `EditTextType` values (`EMAIL`, `PHONE`, `NUMBER`, etc.) have no effect on Compose text fields. Use `Modifier.coralogixMasked()` on fields that do not use `PasswordVisualTransformation` but should still be masked.

## Known Limitations[​](#known-limitations "Direct link to Known Limitations")

### `Modifier.semantics { invisibleToUser() }` — elements invisible to the SDK[​](#modifiersemantics--invisibletouser---elements-invisible-to-the-sdk "Direct link to modifiersemantics--invisibletouser---elements-invisible-to-the-sdk")

Compose's `invisibleToUser()` semantics modifier marks a node as invisible to the accessibility system. Compose prunes these nodes from the accessibility-facing semantics tree before the SDK ever sees them. Because the SDK's Compose integration — hit-testing, interaction tracking, and session replay masking — is built entirely on the semantics tree, **any node marked `invisibleToUser()` is completely invisible to the SDK**.

This has two consequences:

**1. Interaction tracking gaps**

Taps on elements marked `invisibleToUser()` will not produce interaction spans. The hit-test walk never finds the node. This is consistent with how TalkBack and other accessibility services behave, but it is a silent gap.

**2. Masking privacy risk**

If a composable that should be masked (an image, a sensitive text field, or a node with `Modifier.coralogixMasked()`) is also marked `invisibleToUser()`, **it will not be masked in session replay**. The node renders visually and appears in the captured frame, but the SDK's masking pass never sees it. `maskAllImages`, `maskAllTexts`, and `Modifier.coralogixMasked()` are all affected.

**Mitigation:** Apply `Modifier.coralogixMasked()` to a *parent container* that is itself visible in the semantics tree. The parent node will be masked — covering the `invisibleToUser()` children by area, even though those children are not individually traversed.

### `maskAllImages` requires `contentDescription`[​](#maskallimages-requires-contentdescription "Direct link to maskallimages-requires-contentdescription")

`maskAllImages` detects images via `Role.Image` semantics, which is only set when a `contentDescription` is provided on `Image` / `AsyncImage`. Images without a content description are not detected. Use `Modifier.coralogixMasked()` directly on those composables.

### `maskInputFieldsOfTypes` — PASSWORD only for Compose[​](#maskinputfieldsoftypes--password-only-for-compose "Direct link to maskinputfieldsoftypes--password-only-for-compose")

Only `EditTextType.PASSWORD` is evaluated for Compose text fields (detected via `SemanticsProperties.Password` / `PasswordVisualTransformation`). All other `EditTextType` values have no effect on Compose content. Use `Modifier.coralogixMasked()` for other sensitive field types.

## License[​](#license "Direct link to License")

```
Copyright (c) 2025 Coralogix

Licensed under the Apache License, Version 2.0
```
