
Jetpack Compose Previews are a great tool for rapidly iterating over UI design. Understanding their underlying mechanics is optional for everyday tasks but invaluable when facing uncommon problems.
In this article, we’ll explore how Jetpack Compose Previews work. We’ll also cover how to run them using adb, the nuances in a multi-modular project, things we can customize, and even how we can preview a non @Preview annotated composable.
At the end of this article, you’ll find a complete sample project and a few practical use cases where this knowledge proves useful.
Composable preview modes
We’ll categorize preview inspection into two modes:
- Design inspection through the Android Studio design window.
- Runtime inspection by running previews into an emulator or device.
Note: This article focuses exclusively on runtime inspection mode.
How do runtime previews work?
The core component and heart of runtime previews is the androidx.compose.ui.tooling.PreviewActivity. This activity is responsible for hosting the composable annotated with the @Preview annotation, passing its @PreviewParameter data, and setting up the preview appearance according to the properties specified in the @Preview annotation.
PreviewActivity lives in the androidx.compose.ui:ui-tooling artifact, which we usually add as the Gradle dependencies:
implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")
These contain the preview annotations and the required classes that support the Jetpack Compose Preview tooling.
Runtime previews are instrumented APKs
Whenever we run a preview through the run button on the left, it triggers a compilation for the androidTest source set of the module where the preview is defined:

You can verify this by checking the executed Gradle tasks in the build output tab of Android Studio:

The output of the :assembleDebugAndroidTest task is an APK generated for instrumentation. Anecdotally, this APK is used to run the preview; however, there is an exception when the preview is run through the :app module, in which case the APK won’t be used. Since this exception does not significantly impact the overall process, we will assume that the APK is used consistently.
Lastly, Android Studio will install the instrumented APK and launch the PreviewActivity, passing information about which composable preview to render.
Launching ModuleComposablePreview on 'Pixel 7 API 34'.
Starting: Intent {
act=android.intent.action.MAIN
cat=[android.intent.category.LAUNCHER]
cmp=me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity (has extras)
}
Open logcat panel for emulator Pixel 7 API 34
Connected to process 7329 on device 'Pixel_7_API_34 [emulator-5554]'.
Inspecting the AndroidManifest.xml of the preview
Running the ModuleComposablePreview results in the generation of the APK module/build/intermediates/apk/androidTest/debug/module-debug-androidTest.apk.
The
ModuleComposablePreviewis located in the:moduleof the sample project.
By inspecting the AndroidManifest.xml of this APK, we can uncover the following details:
- The
instrumentationtag is present. Even though previews are compiled into an instrumented APK, they run as normal applications, meaning that any instrumentation customization won’t be used:
<instrumentation
android:label="Tests for me.jansv.previewinspect.module.test"
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="me.jansv.previewinspect.module.test"
android:handleProfiling="false"
android:functionalTest="false" />
- By placing an
AndroidManifest.xmlunder themodule/src/androidTestsource set, you can customize certain aspects of the preview, such as theApplicationinstance or the app’s label:
<application
android:label="Module Label"
android:name="me.jansv.previewinspect.module.ModuleInstrumentedApplication"
android:debuggable="true"
android:extractNativeLibs="false">
...
</application>
- The APK does not include an activity with the
android.intent.action.MAINintent action, meaning it won’t appear on the device’s home screen.
These insightful hints suggest we can perform a preview run entirely manually, so let’s find out how!
Job Offers
Running previews without Android Studio
Using the adb command-line tool, you can run a composable preview without Android Studio.
The following steps use the sample project as a playground
Step 1: Generate the preview instrumented APK
./gradlew :module:assembleDebugAndroidTest
Once complete, the task will generate the APK at: module/build/outputs/apk/androidTest/debug/module-debug-androidTest.apk, which we will install as the next step.
Note: This APK is located in a different path than the one generated by Android Studio.
Step 2: Install the APK in a device or emulator.
adb install -r -t \
module/build/outputs/apk/androidTest/debug/module-debug-androidTest.apk
Since this APK does not have an activity with the android.intent.action.MAIN intent action, it will not appear on the device’s home screen.
Step 3: Launch the preview
Until now, we haven’t discussed how PreviewActivity determines which Jetpack Compose preview to render. The activity requires two intent extra parameters to specify the composable to preview:
- composable: A fully qualified path to the
@Previewcomposable function to render. - parameterProviderClassName: A fully qualified class name of the
PreviewParameterProvidersubclass used to populate the preview parameter annotated with@PreviewParameter.
PreviewActivity uses these intent extras to bring these entities to life through reflection.
The preview we want to launch is defined as follows:
// ModuleComposable.kt
package me.jansv.previewinspect.module
@Composable
fun ModuleComposable(text: String? = null) { ... }
@Preview
@Composable
fun ModuleComposablePreview() = ModuleComposable()
@Preview
@Composable
fun ModuleComposablePreview(
@PreviewParameter(TextParameterProvider::class) text: String,
) = ModuleComposable(text = text)
class TextParameterProvider : PreviewParameterProvider<String> {
override val values = (1..5).map { "Param #$it" }.asSequence()
}
The fully qualified path for the ModuleComposablePreview is: me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview.
With this, we can finally launch the preview:
adb shell am start \
-n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \
-e composable "me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview"

With this command, we have started the PreviewActivity as a component of the installed APK. The package name of this APK is me.jansv.previewinspect.module.test which combines the module’s namespace with the .test suffix typically added to instrumented APKs.
This is good 🎉 but let’s push it a bit more…
Additionally, you can run the ModuleComposablePreview with a parameter provider class passing in the parameterProviderClassName:
adb shell am start \
-n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \
-e composable "me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview" \
-e parameterProviderClassName "me.jansv.previewinspect.module.TextParameterProvider"
And yet a bit more…
How about non @Preview composables?
It turns out you can leverage this method to preview a composable that is not annotated with @Preview. For example, you can use the same command to preview ModuleComposable.
adb shell am start \
-n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \
-e composable "me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposable"
Previews in a multi-module setup
Some considerations to keep in mind regarding the differences between running previews in the :app module versus a separate module are:
- The instrumented APK is relevant only for modules that don’t produce an APK, such as those applying the Android library plugin. Since this is not the case for the
:appmodule, the default APK is used instead. - Because the instrumented APK generated for the
:appmodule is not used for previews, no customizations made in theapp/src/androidTestsource set will be applied. - Previews for library modules can be customized through
<module>/src/androidTest. For example, you can modify theApplicationinstance, thePreviewActivitytheme, the label, and more. - If the module you run the preview from introduces
AndroidManifest.xmlplaceholders that are only provided through the:appmodule, the module will need to supply them for the instrumented APK as well for the compilation to work. You can leverage Gradle to supply them conditionally:
// module's build.gradle.kts
androidComponents {
onVariants(selector().withBuildType("debug")) {
it.androidTest?.manifestPlaceholders?.put("placeholder", "value")
}
}
Conclusion
Jetpack Compose Previews are powerful for inspecting and refining your composables. This deep dive covered running previews with adb, understanding their compilation, and previewing composables without the @Preview annotation.
Insights into handling multi-module setups and customizations provide a comprehensive understanding to maximize their utility. With this knowledge, you can streamline your development process and tackle complex preview scenarios confidently.
Thanks for reading and I hope you found this guide useful 🙌🏼
Sample app
https://github.com/janselv/JetpackPreviewInspectSample?source=post_page—–57601ea32ebc——————————–
This article is previously published on proandroiddev.com



