Content Modeling in CMP
Learn how to handle different content type and nestable blocks, render rich text, and use story references to manage content globally in a Compose Multiplatform (CMP) project.
In the existing space, create the following blocks:
- An
articlecontent type block with the following fields:title: Textcontent: Rich text
- A
featured-articlesnestable block with the following field:articles: References
Next, create an Articles folder, open it, and create a few stories that use the article content type.
Finally, add the featured-articles block to the body field of the Home story, and select the created articles to feature.
Create and register blocks
Section titled “Create and register blocks”To add Component subclasses for the new blocks, update the BlockLibrary.kt file in the common source set.
package org.example.project
import kotlinx.serialization.SerialNameimport kotlinx.serialization.Serializableimport com.storyblok.cdn.schema.Componentimport com.storyblok.cdn.schema.RichTextimport com.storyblok.cdn.schema.Story…@Serializable@SerialName("teaser")data class Teaser(val headline: String): Component()
@Serializable@SerialName("article")data class Article(val title: String, val content: RichText) : Component()
@Serializable@SerialName("featured-articles")data class FeaturedArticles(val articles: List<Story<Article>>) : Component()The new classes define the fields of the article and featured-articles blocks:
Article.contentis aRichText, which can represent any rich text node (a document in this case).FeaturedArticles.articlesis aList<Story<Article>>, so the API resolves the referenced stories and returns them. To receive only the UUIDs of the linked stories, change the field type toList<Uuid>.
Next, add composables for these blocks inside the blockProvider in App.kt.
…import androidx.navigation3.ui.NavDisplayimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.Spacerimport androidx.compose.foundation.layout.heightimport androidx.compose.material3.TextButton
… blockProvider = blockProvider( fallback = { it, modifier -> Text("Unknown block type '${it.component}'", modifier) } fallback = { it, modifier -> Text("Unknown block type '${it.component}'", modifier) }, storyLinkListener = { uuid, _ -> backStack.add(StoryKey(uuid = uuid)) } ) { block<Page> { page, modifier -> LazyColumn(modifier) { items(page.body, key = { it.uid }) { Block(it, Modifier.fillMaxWidth()) } } } block<Feature> { feature, modifier -> Text(feature.name, modifier.padding(16.dp)) } block<Teaser> { teaser, modifier -> Text(teaser.headline, modifier.padding(16.dp), style = typography.headlineMedium) } block<Grid> { grid, modifier -> FlowRow(modifier) { grid.columns.forEach { Block(it) } } } block<FeaturedArticles> { featured, modifier -> Column(modifier.padding(16.dp)) { Text("Featured Articles", style = MaterialTheme.typography.titleLarge) Spacer(Modifier.height(8.dp)) featured.articles.forEach { article -> TextButton(onClick = { backStack.add(StoryKey(story = article)) }) { Text(article.content.title) } } } } block<Article> { article, modifier -> Column(modifier.padding(16.dp)) { Text(article.title, style = MaterialTheme.typography.headlineLarge) Spacer(Modifier.height(16.dp)) RichText(article.content, Modifier.fillMaxWidth()) } } }…The blockProvider now registers composables for FeaturedArticles and Article alongside the existing blocks:
- The
block<FeaturedArticles>composable renders links to the featured articles. Selecting a link pushes aStoryKeywith the articleStoryinstance for immediate display of the selected article. - The
block<Article>composable invokesRichText()to render the article’s rich text content.
The storyLinkListener parameter of blockProvider handles story links in rich text by pushing a StoryKey onto the back stack.
Run the app on any target. The home story lists the featured articles, and selecting one opens the article.
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