
Automatic generation of type-safe navigation routes, serializers, per-screen Actions, wrappers and screen mappers from annotated Composable screens, eliminating manual polymorphic serialization and navigation boilerplate.
A powerful KSP-based navigation generator for Compose Multiplatform Navigation 3. It automates route definitions, polymorphic serialization, and screen mapping to provide a type-safe, boilerplate-free navigation experience across Android, iOS, Desktop, and Web.
It includes:
SerializersModule for polymorphic navigation state saving.when(key) block for NavDisplay.Actions classes and CompositionLocals for screen callbacks.implementation("io.github.the-best-is-best:nav3-annotations:1.1.0")Add the KSP processor to your project:
dependencies {
add("kspCommonMainMetadata", "io.github.the-best-is-best:nav3-processor:1.1.0")
// Add for each target to ensure code visibility in IDE
add("kspAndroid", "io.github.the-best-is-best:nav3-processor:1.1.0")
add("kspIosArm64", "io.github.the-best-is-best:nav3-processor:1.1.0")
add("kspIosSimulatorArm64", "io.github.the-best-is-best:nav3-processor:1.1.0")
}./gradlew :shared:assemble@NavGenerate
interface Routes : NavKey@Target(AnnotationTarget.FUNCTION)
annotation class NavDestination(
val name: String = "", // Custom name for the route (e.g. "Splash")
val group: String = "", // Optional nesting group (e.g. "Secure")
val wrapper: String = "" // Optional wrapper function name (e.g. "SecureWrapper")
)@NavGenerate
interface Routes : NavKey@Composable
@NavDestination(name = "Home", group = "Secure", wrapper = "SecureWrapper")
fun HomeScreen(
x: Int, // Data: Automatically added to RoutesDestinations.Secure.Home
onDialogOpen: (Boolean) -> Unit // Action: Automatically added to RoutesHomeActions
) {
Text("Home Screen with value $x")
}@Composable
fun App() {
// rememberRoutesBackStack and RoutesDestinations are auto-generated
val backStack = rememberRoutesBackStack(initialKey = RoutesDestinations.Splash)
var isDialogOpen by remember { mutableStateOf(false) }
// RoutesHomeActions is auto-generated specifically for HomeScreen
val homeActions = RoutesHomeActions(
onDialogOpen = { isDialogOpen = it },
onBack = { backStack.removeLastOrNull() }
)
CompositionLocalProvider(LocalRoutesHomeActions provides homeActions) {
NavDisplay<Routes>(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
entryProvider = { key -> routesEntryProvider(key) } // Auto-generated mapper
)
}
}kotlin {
sourceSets.named("commonMain").configure {
// Ensure the IDE sees generated code
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}
}build/generated/ksp/
polymorphic { subclass(...) } or huge when blocks.wrapper function must take a single @Composable () -> Unit parameter named content.A powerful KSP-based navigation generator for Compose Multiplatform Navigation 3. It automates route definitions, polymorphic serialization, and screen mapping to provide a type-safe, boilerplate-free navigation experience across Android, iOS, Desktop, and Web.
It includes:
SerializersModule for polymorphic navigation state saving.when(key) block for NavDisplay.Actions classes and CompositionLocals for screen callbacks.implementation("io.github.the-best-is-best:nav3-annotations:1.1.0")Add the KSP processor to your project:
dependencies {
add("kspCommonMainMetadata", "io.github.the-best-is-best:nav3-processor:1.1.0")
// Add for each target to ensure code visibility in IDE
add("kspAndroid", "io.github.the-best-is-best:nav3-processor:1.1.0")
add("kspIosArm64", "io.github.the-best-is-best:nav3-processor:1.1.0")
add("kspIosSimulatorArm64", "io.github.the-best-is-best:nav3-processor:1.1.0")
}./gradlew :shared:assemble@NavGenerate
interface Routes : NavKey@Target(AnnotationTarget.FUNCTION)
annotation class NavDestination(
val name: String = "", // Custom name for the route (e.g. "Splash")
val group: String = "", // Optional nesting group (e.g. "Secure")
val wrapper: String = "" // Optional wrapper function name (e.g. "SecureWrapper")
)@NavGenerate
interface Routes : NavKey@Composable
@NavDestination(name = "Home", group = "Secure", wrapper = "SecureWrapper")
fun HomeScreen(
x: Int, // Data: Automatically added to RoutesDestinations.Secure.Home
onDialogOpen: (Boolean) -> Unit // Action: Automatically added to RoutesHomeActions
) {
Text("Home Screen with value $x")
}@Composable
fun App() {
// rememberRoutesBackStack and RoutesDestinations are auto-generated
val backStack = rememberRoutesBackStack(initialKey = RoutesDestinations.Splash)
var isDialogOpen by remember { mutableStateOf(false) }
// RoutesHomeActions is auto-generated specifically for HomeScreen
val homeActions = RoutesHomeActions(
onDialogOpen = { isDialogOpen = it },
onBack = { backStack.removeLastOrNull() }
)
CompositionLocalProvider(LocalRoutesHomeActions provides homeActions) {
NavDisplay<Routes>(
backStack = backStack,
onBack = { backStack.removeLastOrNull() },
entryProvider = { key -> routesEntryProvider(key) } // Auto-generated mapper
)
}
}kotlin {
sourceSets.named("commonMain").configure {
// Ensure the IDE sees generated code
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}
}build/generated/ksp/
polymorphic { subclass(...) } or huge when blocks.wrapper function must take a single @Composable () -> Unit parameter named content.