kotlinrouteswebassemblycompose-multiplatformnavigation-compose

Can Compose Multiplatform navigate and change routes on Web while maintaining URL state?


I've been exploring Compose Multiplatform and went through the documentation, particularly Compose Multiplatform Navigation Routing. Despite the library being supported on the web, the docs mention that navigation and handling via routing is not yet supported.

Additionally, I reviewed Getting Started with Compose for Web and noticed that none of the demo projects showcase route navigation or changing the URL state dynamically. For instance, in a scenario where I click on a list item, I expect to navigate to that item's page with a new URL like "https://example.com/list/id", but this isn't demonstrated.

I've also checked the Compose for Web Slack channel but didn't find relevant queries. The documentation suggests other libraries like Voyager, Decompose, Appyx, PreCompose, and Circuit, but I haven't tried any yet.

Goal: I aim to run a single project on Web, iOS, and Android, meaning it should compile seamlessly across all platforms. Theoretically, I assume handling this through communication with native platforms is possible. Still, I'd appreciate confirmation on whether achieving this solely with Compose Multiplatform tools is not feasible at the moment.

If anyone has successfully implemented this or has insights into whether it’s possible with the mentioned libraries, please share your experience.


Solution

  • I created a YouTrack ticket (https://youtrack.jetbrains.com/issue/CMP-7336/Add-Routing-Support-for-Compose-Multiplatform-Web) for this issue and received a response indicating that this feature has been recently released in an “alpha” version. Here’s how to implement it:

    Overview

    The functionality is described in the corresponding pull request (https://github.com/JetBrains/compose-multiplatform-core/pull/1621#issue-2570681373). However, there were gaps in the documentation and setup. Here’s a step-by-step guide to get it working.

    Setup

    1. Follow the official Compose Navigation Routing documentation (https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-navigation-routing.html).

    2. Use the dependency:

    implementation("org.jetbrains.androidx.navigation:navigation-compose:2.8.0-alpha11")
    

    I found this version recommendation in this comment (https://youtrack.jetbrains.com/issue/CMP-4983/is-compose-multiplatform-for-web-wasmJs-support-routing#focus=Comments-27-11342053.0-0). Do not follow auto-import suggestions in Android Studio as they will add androidx.navigation:navigation-compose, which doesn’t work for Compose Multiplatform.

    1. Ensure you’re using:

      compose-multiplatform = "1.8.0-alpha01"

    The default project wizard sets the stable version (1.7.0), which might doesn’t support this feature.

    What Didn’t Work • The sample project (https://github.com/JetBrains/compose-multiplatform/tree/master/examples/nav_cupcake) showcases web URL support but differs from the example in the pull request and was not functional for me.

    Code Example

    Here’s how I implemented it:

    commonMain

    1. Define an enum for navigation destinations:
    enum class NavigationDestination {
        Landing,
        Book,
    }
    
    1. Implement the navigation logic:
    import androidx.compose.runtime.Composable
    import androidx.navigation.NavHostController
    import androidx.navigation.compose.NavHost
    import androidx.navigation.compose.composable
    import androidx.navigation.compose.rememberNavController
    import org.jetbrains.compose.ui.tooling.preview.Preview
    
    @Composable
    @Preview
    fun App(
        navController: NavHostController = rememberNavController()
    ) {
        AppTheme {
            NavHost(
                navController = navController,
                startDestination = NavigationDestination.Landing.name
            ) {
                composable(route = NavigationDestination.Landing.name) {
                    LandingPage(onNavigateToBook = {
                        navController.navigate(NavigationDestination.Book.name)
                    })
                }
                composable(route = NavigationDestination.Book.name) {
                    Page()
                }
            }
        }
    }
    

    wasmJsMain

    Set up the web-specific logic:

    import androidx.compose.runtime.LaunchedEffect
    import androidx.compose.ui.ExperimentalComposeUiApi
    import androidx.compose.ui.window.ComposeViewport
    import androidx.navigation.ExperimentalBrowserHistoryApi
    import androidx.navigation.NavHostController
    import androidx.navigation.bindToNavigation
    import androidx.navigation.compose.rememberNavController
    import kotlinx.browser.document
    import kotlinx.browser.window
    import org.w3c.dom.HTMLElement
    
    @OptIn(ExperimentalComposeUiApi::class, ExperimentalBrowserHistoryApi::class)
    fun main() {
        val body: HTMLElement = document.body ?: return
        ComposeViewport(body) {
            val navController: NavHostController = rememberNavController()
            App(navController = navController)
            LaunchedEffect(Unit) {
                window.bindToNavigation(navController)
            }
        }
    }
    

    With this setup, URL-based navigation is now functional for Compose Multiplatform Web in an alpha release.