Files
vnidrop/.codex/skills/compose-skill/references/navigation.md
Hammed Abass fd4fff2c86 docs: add compose development guidelines and skills
Added a comprehensive collection of Compose development guidelines, best practices, and skill definitions in the .codex directory.

Co-authored-by: Junie <junie@jetbrains.com>
2026-07-07 16:55:43 +02:00

5.0 KiB

Navigation

Shared navigation concepts for Nav 2 and Nav 3. Load first, then see version-specific references.

References:

Nav 2 vs Nav 3 Decision Guide

Criterion Nav 3 (NavDisplay) Nav 2 (NavHost / NavController)
Back stack ownership You own it (SnapshotStateList) Library owns it (NavController)
Navigation model List manipulation — add(), removeLastOrNull() Imperative — navigate(), popBackStack()
MVI alignment Natural — back stack is state you mutate Requires bridging — controller calls in effect handlers
Deep link parsing You parse URIs, construct back stack manually Built-in NavDeepLink parsing
Scenes / adaptive layouts First-class: dialog, bottom sheet, list-detail Manual: separate composable overlays
CMP support Full (Android, iOS, Desktop, Web) Android-only (JetBrains forks exist but differ)
Maturity Newer — verify artifact stability for production Stable, battle-tested
Fragment interop None Full Fragment/Activity integration

When to use Nav 3:

  • New Compose projects following MVI architecture
  • Compose Multiplatform projects targeting multiple platforms
  • Projects wanting direct back stack control as state
  • Projects needing adaptive layout scenes (list-detail, dialog, bottom sheet)

When to use Nav 2:

  • Existing codebases already built on NavHost/NavController
  • Projects requiring built-in deep link parsing via NavDeepLink
  • Hybrid Compose + Fragment apps where Nav 2 provides Fragment integration
  • Teams that prefer the declarative NavGraph DSL

Navigation in MVI

The architectural rule: ViewModels emit semantic effects; the route layer handles navigation. This rule applies identically to both Nav 2 and Nav 3.

sealed interface ItemEffect {
    data object NavigateBack : ItemEffect
    data class OpenDetails(val id: String) : ItemEffect
}

// Nav 3 route layer — manipulates back stack
CollectEffect(viewModel.effect) { effect ->
    when (effect) {
        is ItemEffect.NavigateBack -> backStack.removeLastOrNull()
        is ItemEffect.OpenDetails -> backStack.add(Details(effect.id))
    }
}

// Nav 2 route layer — calls NavController
CollectEffect(viewModel.effect) { effect ->
    when (effect) {
        is ItemEffect.NavigateBack -> navController.navigateUp()
        is ItemEffect.OpenDetails -> navController.navigate(Detail(effect.id))
    }
}

Rules

  • Never call navigation during composition — always in LaunchedEffect or event handler callbacks
  • Never pass the back stack (Nav 3) or NavController (Nav 2) to the ViewModel or leaf composables
  • ViewModel emits semantic effects (NavigateBack, OpenDetails(id))
  • Route/navigation layer translates effects to navigation calls
  • Keep navigation logic at the route boundary, not in screens or leaves

Anti-Patterns

Anti-pattern Applies to Why it hurts Better replacement
Navigating during composition Both Triggers on every recomposition, causes infinite loops Navigate in LaunchedEffect or event handler callbacks
Passing NavController/back stack to ViewModel Both Violates MVI boundary, navigation becomes business logic ViewModel emits semantic effects; route handles navigation
String-based routes without type safety Both No compile-time checking, argument mismatch at runtime @Serializable data classes/objects
Missing onBack handler Nav 3 System back gesture does nothing Always provide onBack = { backStack.removeLastOrNull() }
Globally-scoped ViewModel for per-screen data Both Data leaks across screens, not cleared on pop Entry-scoped VMs (Nav 3 decorators) or destination-scoped VMs (Nav 2)
Recreating back stacks on tab switch Both Loses user navigation history within tabs Persistent per-tab stacks (Nav 3) or saveState/restoreState (Nav 2)
Missing entry decorators Nav 3 ViewModels leak, saveable state lost Always include both rememberSaveableStateHolderNavEntryDecorator and rememberViewModelStoreNavEntryDecorator
Using Nav 2 in new MVI codebases Nav 3 preferred Nav 3's user-owned back stack aligns better with MVI state ownership Prefer Nav 3 NavDisplay for new MVI-first projects; Nav 2 remains valid for existing codebases

Version-Specific References

Load the file that matches your task: