Kotlin Multiplatform Library for Onboarding Showcases
A Kotlin Multiplatform library for creating onboarding showcases, UI highlights, and guided walkthroughs in Compose. Use it to spotlight key UI components, guide users step-by-step, and deliver polished intro experiences on both Android and iOS with simple, declarative APIs.
Key Features:
- Multiplatform Support: Write your showcase logic once in commonMain and run it on both Android and iOS.
- Declarative API: Define showcase steps with simple composables. Use the .captureBounds() modifier to easily target any UI element.•Customizable Highlights: Control the look of your highlighted area with customizable shapes (rounded rectangle, circle) and styles.
- Flexible Content: Display any composable content for each step—not just a simple tooltip. Use Column, Button, or any other composable to create rich, interactive guides.
- Intelligent Tooltip Positioning: The built-in Tooltip automatically repositions itself to avoid screen edges, ensuring your content is always visible.
- Showcase Controller: A simple ShowcaseController to easily start and manage the sequence of your showcase steps.
Demo

Installation
First, add the dependency to your commonMain source set in your build.gradle.kts file:
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.github.suwasto:kmp-showcase-compose:0.1.0")
}
}
}
For a complete example, check out the composeApp directory in the repository, which contains the usage of UI components and utilities for showcase onboarding.
Step 1: Set up ShowcaseHost and Controller
First, wrap your screen's content with ShowcaseHost and create a ShowcaseController to manage the showcase.
@Composable
fun MyScreen() {
val showcaseController = rememberShowcaseController()
ShowcaseHost(controller = showcaseController) {
}
}
Step 2: Capture Component Bounds
To highlight a UI element, you need to capture its layout bounds. The captureBounds modifier makes this easy. It’s best to use a map to store the bounds (Rect) of each component you want to highlight.
enum class ShowcaseHighlight {
Search, Bag
}
val showcaseLayouts = remember { mutableStateMapOf<ShowcaseHighlight, Rect>() }
IconButton(
modifier = Modifier.captureBounds { rect ->
showcaseLayouts[ShowcaseHighlight.Search] = rect
},
onClick = { }
) {
Icon(Icons.Default.Search, contentDescription = "Search")
}
Step 3: Define ShowcaseSteps
Once you have the bounds, you can define the steps for your showcase. A ShowcaseStep describes a single highlighted element and its accompanying content. The content can be any composable, not just a Tooltip. Using derivedStateOf ensures that your steps update automatically when the layout bounds are resolved.
Define the steps inside your main composable so you can access the showcaseController if needed (e.g., for a "Next" button).
val showcaseSteps by remember {
derivedStateOf {
listOfNotNull(
showcaseLayouts[ShowcaseHighlight.Search]?.let { rect ->
ShowcaseStep(
targetRect = rect,
content = { SearchTooltip(showcaseController) }
)
},
showcaseLayouts[ShowcaseHighlight.BAG]?.let { rect ->
ShowcaseStep(
targetRect = rect,
content = { BagTooltip(showcaseController) }
)
}
)
}
}
Step 4: Start the Showcase
Finally, use a LaunchedEffect to start the showcase once the steps are defined. To ensure the showcase runs only once, you can use a rememberSaveable flag.
LaunchedEffect(showcaseSteps, shouldShowOnboardingShowcases) {
if (shouldShowOnboardingShowcases) {
showcaseController.start(showcaseSteps)
}
}
Putting It All Together
Here’s a complete example that combines all the steps into a single, self-contained composable.
Customizing the Tooltip
Inside the content block of a ShowcaseStep, you can design any Compose UI. You have access to the controller to move to the next step or dismiss the tour.
Standar Onboarding Showcase Sample

Animated Overlay Showcase Onboarding Sample
fun getSwocaseStepSearch(
rect: Rect,
showcaseController: ShowcaseController
): ShowcaseStep {
var showcaseStyle: ShowcaseStyle = ShowcaseStyle.Standard(shape = ShowcaseShape.Circle)
var dimColor: Color = Color.Black.copy(alpha = 0.7f)
return ShowcaseStep(
style = showcaseStyle,
rect = rect,
enableDimAnim = true,
dimColor = dimColor,
onClickHighlight = {},
content = { highlightRect ->
},
)
}

Water Ripple Cutout Animation

Pulsing Circle Cutout Animation
fun getSwocaseStepSearch(
rect: Rect,
showcaseController: ShowcaseController
): ShowcaseStep {
var showcaseStyle: ShowcaseStyle = ShowcaseStyle.PulsingCircle(color = Color.Cyan)
var dimColor: Color = Color.Black.copy(alpha = 0.7f)
return ShowcaseStep(
style = showcaseStyle,
rect = rect,
enableDimAnim = true,
dimColor = dimColor,
onClickHighlight = {},
content = { highlightRect ->
},
)
}

ShowcaseStep Parameters
Tooltip Parameters
📄 License
Copyright 2025 Anang Suwasto
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance the License.
You may obtain a copy of the License at
http:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.