Available on Maven Central

Guide users.
Highlight what matters.

Beautiful, customizable coach marks and product tours for Compose Multiplatform. Designed to feel native, polished, and easy to maintain.

implementation("io.github.tharukack:guidekit:1.1.0")
  • Android
  • iOS
  • MIT licensed
GuideKit Essentials tour highlighting UI elements in a Compose application

SpotlightAny UI element

Smart arrowsCurved or direct

Step throughComplete product tours

Cross-platformAndroid & iOS

See it in motion

Guidance that feels part of your app.

GuideKit renders above your existing Compose UI. Your screens stay intact while targets, arrows, and instruction cards work together.

01

Direct attention without interrupting the experience.

Highlight a card, a button, an avatar, or any measured composable. GuideKit handles the overlay while your app keeps control of content and completion.

Rounded targets Curved arrows Auto-scroll
GuideKit highlighting the measured status strip with a dashed arrow
Measured targetsHighlight real composables and explain step ownership.
GuideKit highlighting the main sample card with a coach mark
Polished defaultsLayered glow, cutout, arrow, progress, and instruction card.
GuideKit Studio screen using a coral color theme
Any color themeApply a visual system globally and refine individual steps.

Installation

Add one dependency to shared Compose UI.

GuideKit is published on Maven Central. Add it to the source set that renders your tours.

Compose Multiplatform · commonMain
kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("io.github.tharukack:guidekit:1.1.0")
        }
    }
}
Compose module
implementation("io.github.tharukack:guidekit:1.1.0")

Quick start

Your first guide in three clear steps.

GuideKit belongs beside the content in the main composable that occupies the full screen. A Scaffold is optional and does not change the setup.

01

Keep state in the parent screen

Own each target's measured bounds at the full-screen level. This example uses saveable visibility; use DataStore, a database, or any persistence you prefer to stop completed guides appearing again.

MainScreen.kt
var targetBounds by remember { mutableStateOf<Rect?>(null) }
var showGuide by rememberSaveable { mutableStateOf(true) }
02

Measure the real target

Pass a bounds callback into the nested composable. Attach onGloballyPositioned to the exact component being highlighted, then send its boundsInRoot() value back to the parent.

MainScreenContent.kt
@Composable
fun MainScreenContent(
    onTargetBoundsChanged: (Rect?) -> Unit,
) {
    Button(
        onClick = { /* action */ },
        modifier = Modifier.onGloballyPositioned { coordinates ->
            onTargetBoundsChanged(coordinates.boundsInRoot())
        },
    ) { Text("Create item") }
}
03

Render GuideKit after the content

Pass the callback into the child and render GuideKit after the screen content in the same full-size parent. This ensures the overlay draws above the entire screen.

MainScreen.kt
@Composable
fun MainScreen() {
    var targetBounds by remember { mutableStateOf<Rect?>(null) }
    var showGuide by rememberSaveable { mutableStateOf(true) }

    Box(Modifier.fillMaxSize()) {
        MainScreenContent(
            onTargetBoundsChanged = { targetBounds = it },
        )

        if (showGuide) {
            GuideKit(
                steps = listOf(
                    GuideKitStep(
                        targetBounds = targetBounds,
                        title = "Create your first item",
                        description = "Tap here to start a new workflow.",
                        arrowConfigOverride = GuideKitArrowConfigOverride(
                            from = GuideKitAnchor.TopRight,
                            to = GuideKitAnchor.BottomCenter,
                        ),
                    ),
                ),
                onSkipped = { showGuide = false },
                onFinished = { showGuide = false },
                modifier = Modifier.fillMaxSize(),
            )
        }
    }
}

Nested target? Keep it where it belongs. Send targetBounds from that component to the full-screen parent through a callback such as onTargetBoundsChanged.

Customization

Start global. Get precise per step.

Set a consistent tour style once, then override only the exact arrow, highlight, or instruction-card properties a step needs.

01

Add multiple steps

Add one GuideKitStep per target. The list order is the tour order; GuideKit handles next, previous, and finish navigation.

ThreeStepGuide.kt
val guideSteps = listOf(
    GuideKitStep(
        targetBounds = createButtonBounds,
        title = "Create your first item",
        description = "Tap here to start a new workflow.",
        arrowConfigOverride = GuideKitArrowConfigOverride(
            from = GuideKitAnchor.TopRight,
            to = GuideKitAnchor.BottomCenter,
        ),
    ),
    GuideKitStep(
        targetBounds = activityCardBounds,
        title = "Track your progress",
        description = "Your latest activity appears here.",
        arrowConfigOverride = GuideKitArrowConfigOverride(
            from = GuideKitAnchor.BottomLeft,
            to = GuideKitAnchor.TopCenter,
        ),
    ),
    GuideKitStep(
        targetBounds = profileButtonBounds,
        title = "Make it yours",
        description = "Customize your experience here.",
        arrowConfigOverride = GuideKitArrowConfigOverride(
            from = GuideKitAnchor.TopRight,
            to = GuideKitAnchor.CenterLeft,
        ),
    ),
)
02

Global and step-specific styles

GuideKitStyle supplies tour-wide defaults. Step override objects change only their explicitly supplied properties, so unrelated global colors, shapes, strokes, padding, and shadows remain intact.

Global style
val style = GuideKitStyle(
    accentColor = Color(0xFF7C4DFF),
    overlayColor = Color.Black.copy(alpha = 0.72f),
    arrowConfig = GuideKitArrowConfig(
        lineStyle = GuideKitArrowLineStyle.SpacedDash,
    ),
    targetHighlight = GuideKitTargetHighlightStyle(
        shape = GuideKitTargetHighlightShape.RoundedRect,
    ),
)
Step override
GuideKitStep(
    targetBounds = avatarBounds,
    title = "Your profile",
    description = "Manage it here.",
    arrowConfigOverride = GuideKitArrowConfigOverride(
        lineStyle = GuideKitArrowLineStyle.Solid,
    ),
    targetHighlightOverride = GuideKitTargetHighlightStyleOverride(
        shape = GuideKitTargetHighlightShape.Circle,
    ),
)
Library defaultsGlobal GuideKitStyleExplicit step overrides

Ordinary override properties default to null, which means inherit. When null is intentional, such as removing a global border, use borderOverride = GuideKitOverride.Value(null).

03

Step text

Highlight one or many exact phrases with a single API. If no button label is supplied, GuideKit uses Next and changes the final step to Got it.

GuideKitStep(
    targetBounds = activityBounds,
    title = "Never miss an update",
    description = "Important activity and urgent alerts appear here.",
    descriptionHighlights = listOf("Important activity", "urgent alerts"),
    primaryButtonText = "Continue",
)
04

Arrows

from is the connection point on the instruction card; to is the connection point on the highlighted target. Choose any of nine anchors, deterministic curves, layered strokes, arrowheads, and named line styles.

TopLeftTopCenterTopRightCenterLeftCenterCenterRightBottomLeftBottomCenterBottomRight
Solid
SpacedDash
Dotted
ShortDash
MediumDash
LongDash
DashDot
GuideKitArrowConfigOverride(
    from = GuideKitAnchor.TopRight,
    to = GuideKitAnchor.CenterLeft,
    curveSeed = 3,
    lineStyle = GuideKitArrowLineStyle.MediumDash,
    arrowHead = GuideKitArrowHead.BothSides,
    arrowHeadLength = 38,
    arrowHeadAngleDegrees = 30,
    strokes = listOf(
        GuideKitArrowStroke(width = 9, color = Color.Black.copy(alpha = 0.22f)),
        GuideKitArrowStroke(width = 5, color = Color(0xFF7C4DFF)),
    ),
)
05

Target highlights

Use a rounded rectangle for cards and controls or a circle for avatars, icons, and floating action buttons. Customize cutout, integer padding and stroke widths, corner radius, layered glow, and borders.

Weekly progress12 tasks completed +18%
RoundedRect
MC Maya Chen Product designer
Circle
GuideKitTargetHighlightStyleOverride(
    shape = GuideKitTargetHighlightShape.Circle,
    padding = 16,
    glowStrokes = listOf(
        GuideKitTargetHighlightStroke(width = 30, alpha = 0.14f),
        GuideKitTargetHighlightStroke(width = 8, alpha = 0.55f),
    ),
    borderWidth = 3,
)
06

Instruction cards

Control alignment, outer and inner padding, constraints, shape, colors, border, Material elevation, and the optional custom shadow globally or per step.

GuideKitInstructionBoxStyleOverride(
    alignment = Alignment.BottomCenter,
    outerPadding = PaddingValues(20.dp),
    contentPadding = PaddingValues(horizontal = 24.dp, vertical = 20.dp),
    fillMaxWidth = false,
    maxWidth = 420.dp,
    shape = RoundedCornerShape(28.dp),
    containerColor = Color(0xFF17132B),
    contentColor = Color.White,
    shadow = GuideKitInstructionBoxShadow(elevation = 32.dp),
)
07

Automatic scrolling

Auto-scroll is enabled per step by default. Supply onScrollBy so GuideKit can ask the host scroll state to keep the target visible and clear of the instruction card.

GuideKit(
    steps = guideSteps,
    onScrollBy = { deltaPx -> scrollState.animateScrollBy(deltaPx) },
    onFinished = { showGuide = false },
)

// Fixed target, such as a floating action button:
GuideKitAutoScrollConfig(enabled = false)
08

Navigation and callbacks

Choose the starting step, toggle progress, observe step changes, and persist skipped or completed tours. Swiping the card right returns to the previous step.

GuideKit(
    steps = guideSteps,
    initialStepIndex = 0,
    showStepIndicator = true,
    onStepChanged = { index -> analytics.track("guide_step_${index + 1}") },
    onSkipped = {
        showGuide = false
        onboardingStore.markGuideCompleted()
    },
    onFinished = {
        showGuide = false
        onboardingStore.markGuideCompleted()
    },
)

API reference

Every public option and its default.

A compact reference for the current 1.1.0 API. Open only the group you need.

GuideKitOverlay, navigation, and lifecycle9 options
OptionDefaultPurpose
stepsRequiredOrdered guide steps; an empty list renders nothing.
modifierModifierUse fillMaxSize() at screen level.
initialStepIndex0Starting step; safely clamped.
showStepIndicatortrueShows progress dots.
styleGuideKitStyle()Global visual defaults.
onStepChanged{}Reports the zero-based active index.
onScrollBy{ 0f }Delegates scrolling to the host.
onSkippednullWhen provided, shows close and handles skip.
onFinishedRequiredRuns after the final primary action.
GuideKitStepContent and per-step overrides10 options
OptionDefaultPurpose
targetBoundsRequiredMeasured target bounds.
titleRequiredCard title.
descriptionRequiredCard body.
primaryButtonTextnull → Next / Got itCustom primary label.
descriptionHighlightsemptyList()Exact phrases to emphasize.
instructionBottomPadding104.dpDefault card bottom space.
arrowConfigOverridenull → inherit globalOverrides supplied arrow properties.
targetHighlightOverridenull → inherit globalOverrides supplied highlight properties.
instructionBoxOverridenull → inherit globalOverrides supplied card properties.
autoScrollGuideKitAutoScrollConfig()Step scroll behavior.
GuideKitArrowConfigOverridePartial per-step arrow changes12 options
OptionDefaultPurpose
enabled, from, tonull → inheritVisibility and connection anchors.
curveSeed, minVisibleDistancenull → inheritPath variation and minimum distance.
lineStyle, strokes, strokeCapnull → inheritArrow body appearance.
arrowHead, arrowHeadLength, arrowHeadAngleDegrees, arrowHeadStrokesnull → inheritArrowhead placement and appearance.
GuideKitTargetHighlightStyleOverridePartial per-step highlight changes12 options
OptionDefaultPurpose
enabled, shape, cutoutEnablednull → inheritVisibility, geometry, and cutout.
padding, cornerRadius, glowStrokesnull → inheritHighlight sizing and glow.
borderColor, borderWidthnull → inheritMain border changes.
borderColorOverrideGuideKitOverride.InheritUse Value(null) to restore accent resolution.
innerBorderColor, innerBorderWidth, innerBorderInsetnull → inheritInner border changes.
GuideKitInstructionBoxStyleOverridePartial per-step card changes16 style properties
OptionDefaultPurpose
alignment, contentPadding, fillMaxWidth, shapenull → inheritPlacement and layout.
outerPadding, minWidth, maxWidth, minHeight, maxHeightnull → inheritDirect non-null spacing and constraints.
containerColor, contentColor, border, shadownull → inheritDirect non-null surface values.
tonalElevation, shadowElevation, modifiernull → inheritElevation and modifier changes.
outerPaddingOverride, size ...Override, color ...Override, borderOverride, shadowOverrideGuideKitOverride.InheritExplicitly set nullable values, including Value(null).
GuideKitStyleGlobal visual defaults13 options
OptionDefault
accentColorColor(0xFF5ED5B3)
overlayColorBlack at 0.68f
titleColornull → theme onSurface
descriptionColornull → theme onSurfaceVariant
highlightedDescriptionColornull → accent
stepIndicatorActiveColornull → accent
stepIndicatorInactiveColornull → theme outlineVariant
primaryButtonContainerColornull → accent
primaryButtonContentColorColor(0xFF062D25)
skipIconTintnull → theme onSurfaceVariant
arrowConfigGuideKitArrowConfig()
targetHighlightGuideKitTargetHighlightStyle()
instructionBoxGuideKitInstructionBoxStyle()
GuideKitArrowConfigPath, strokes, and arrowhead13 options
OptionDefault
enabledtrue
fromTopCenter
toBottomCenter
curveSeed0
minVisibleDistance40.dp
lineStyleSpacedDash
strokesWidths 9, 6, 2
strokeCapStrokeCap.Round
arrowHeadTargetSide
arrowHeadLength38
arrowHeadAngleDegrees30
arrowHeadStrokesWidths 9, 6, 2
GuideKitArrowStroke.alpha1f
GuideKitTargetHighlightStyleCutout, shape, glow, and borders11 options
OptionDefault
enabledtrue
shapeRoundedRect
cutoutEnabledtrue
padding10
cornerRadius28.dp
glowStrokesWidths/alpha 30/.11, 22/.18, 14/.30, 8/.45
borderColornull → accent
borderWidth3
innerBorderColorWhite at 0.7f
innerBorderWidth1
innerBorderInset2
GuideKitInstructionBoxStylePlacement, sizing, surface, and shadow16 options
OptionDefault
alignmentAlignment.BottomCenter
outerPaddingnull → 18.dp sides + step bottom
contentPadding22.dp horizontal, 24.dp vertical
fillMaxWidthtrue
minWidth, maxWidthnull
minHeight, maxHeightnull
shapeRoundedCornerShape(30.dp)
containerColornull → theme surface
contentColornull → theme onSurface
border1.dp accent at 0.28f
tonalElevation0.dp
shadowElevation26.dp
modifierModifier
shadowGuideKitInstructionBoxShadow()
shadow.elevation30.dp
shadow colorsBlack at 0.42f
GuideKitAutoScrollConfigPer-step visibility spacing3 options
OptionDefaultPurpose
enabledtrueEnables automatic scroll requests.
minTopVisibleDistancenull → arrow distance + 1.dpSafe space above the target.
spacingnull → arrow distance + 1.dpSpace between target and card.
EnumsAll named choices4 groups
GuideKitAnchor

TopLeft, TopCenter, TopRight, CenterLeft, Center, CenterRight, BottomLeft, BottomCenter, BottomRight

GuideKitArrowLineStyle

Solid, SpacedDash, Dotted, ShortDash, MediumDash, LongDash, DashDot

GuideKitArrowHead

None, TargetSide, InstructionBoxSide, BothSides

GuideKitTargetHighlightShape

RoundedRect, Circle

Clear ownership

Your app owns intent.
GuideKit owns the tour.

No screen takeover or separate overlay architecture. Provide real target bounds and lifecycle callbacks; GuideKit handles the experience.

Explore the sample app
GuideKit owns
  • Step navigation
  • Highlight drawing
  • Arrow rendering
  • Instruction cards
  • Auto-scroll calculations
Your app owns
  • Step definitions
  • Target measurement
  • Visibility state
  • Persistence
  • Analytics callbacks

Platform support

One shared API. Native-feeling guidance.

Android and iOS are supported today. Desktop and web are on the roadmap.

AndroidSupported
iOSSupported
DesktopPlanned
WebPlanned

Ready when your users are

Make every first experience
feel obvious.

Add your first coach mark in minutes, then shape every detail when you need to.