Dynamic Navigation in CMP
Use Navigation 3 to set up a catch-all navigation strategy in your Compose Multiplatform (CMP) project and render new stories dynamically.
Add dependencies
Section titled “Add dependencies”In the version catalog TOML file, add the Navigation 3 library.
[versions]agp = "9.1.1"…storyblok = "0.7.1"navigation3 = "1.1.1"
[libraries]…storyblok-material3 = { module = "com.storyblok:storyblok-material3", version.ref = "storyblok" }navigation3-ui = { module = "org.jetbrains.androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
[plugins]androidApplication = { id = "com.android.application", version.ref = "agp" }…To add the dependency, update shared/build.gradle.kts.
…sourceSets { commonMain.dependencies { … implementation(libs.storyblok.material3) implementation(libs.navigation3.ui) }}Now reload the Gradle project to download the added dependencies and re-analyze the project.
Create a story navigation key
Section titled “Create a story navigation key”Create a StoryKey.kt file in the common source set to define a navigation key that can represent any story, identified by either its UUID or slug.
package org.example.project
import androidx.navigation3.runtime.NavKeyimport androidx.savedstate.serialization.SavedStateConfigurationimport com.storyblok.cdn.schema.Componentimport com.storyblok.cdn.schema.Storyimport kotlinx.serialization.EncodeDefaultimport kotlinx.serialization.Serializableimport kotlinx.serialization.Transientimport kotlinx.serialization.modules.SerializersModuleimport kotlinx.serialization.modules.polymorphicimport kotlinx.serialization.modules.subclassimport kotlin.uuid.Uuid
@Serializabledata class StoryKey( @Transient val story: Story<out Component>? = null, @EncodeDefault val uuid: Uuid? = story?.uuid, @EncodeDefault val slug: String? = story?.slug,) : NavKey
val HomeKey = StoryKey(slug = "home")
val NavConfiguration = SavedStateConfiguration { serializersModule = SerializersModule { polymorphic(NavKey::class) { subclass(StoryKey::class, StoryKey.serializer()) } }}StoryKey is a NavKey that persists the uuid or slug of a story to the back stack. You can also construct a StoryKey with a Story instance for immediate display when navigating between screens.
Display the back stack
Section titled “Display the back stack”In your App.kt file, add the code to initialize and use a NavDisplay to render the top of the back stack.
…import androidx.compose.ui.unit.dpimport androidx.compose.runtime.rememberimport androidx.navigation3.runtime.entryProviderimport androidx.navigation3.runtime.rememberNavBackStackimport androidx.navigation3.ui.NavDisplay
@Composablefun App() { MaterialTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
val backStack = rememberNavBackStack(NavConfiguration, HomeKey)
Storyblok( accessToken = "YOUR_ACCESS_TOKEN", version = Draft, region = EU, // Choose the correct region from your Space. … ) { val story by story("home").collectAsStateWithLifecycle(null) story?.content?.let { Block(it, Modifier.padding(innerPadding)) } NavDisplay( backStack, Modifier.padding(innerPadding), entryProvider = entryProvider { entry<StoryKey> { key -> val story by remember(key) { when (key.uuid) { null -> story(slug = key.slug!!) else -> story(uuid = key.uuid) } }.collectAsStateWithLifecycle(key.story)
story?.content?.let { Block(it) } } } ) } } }}The single entry<StoryKey> handles every possible story destination. It fetches the story by UUID or slug, populating the initial value of collectAsStateWithLifecycle with the key’s story instance so available content appears immediately.
NavDisplay also handles the platform’s back gesture: the system back button on Android, and the back swipe on iOS.
With this approach, the project renders any new story in the space as soon as the app pushes its key onto the back stack.
Related resources
Section titled “Related resources”Was this page helpful?
This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window).Terms of Service (opens in a new window) apply.
Get in touch with the Storyblok community