---
title: Content Modeling in Android
description: Learn how to handle custom blocks, render rich text, and use story references to manage content across your Android project.
url: https://www.storyblok.com/docs/guides/android/content-modeling
---

# Content Modeling in Android

Learn how to handle different content-type and nestable blocks, render rich text, and use story references to manage content globally.

## Setup

In the existing space, create the following blocks:

-   An `article` content type block with the following fields:
    -   `title`: Text
    -   `content`: Rich text
-   A `featured-articles` nestable block with the following field:
    -   `articles`: References

> [!NOTE]
> Learn more about fields in the [fields concept](/docs/concepts/fields).

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

Update the `BlockLibrary.kt` file to add [`Component`](https://storyblok.github.io/storyblok-kotlin/content-api-client/com.storyblok.cdn.schema/-component/index.html) subclasses for the new blocks.

app/src/main/java/com/example/myapplication/BlockLibrary.kt

```kotlin
package com.example.myapplication

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import com.storyblok.cdn.schema.Component
import com.storyblok.cdn.schema.RichText
import 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 fields of the `article` and `featured-articles` blocks are defined as follows:

-   `Article.content` is defined as [`RichText`](https://storyblok.github.io/storyblok-kotlin/content-api-client/com.storyblok.cdn.schema/-rich-text/index.html) which can represent any rich text node (a [document](https://storyblok.github.io/storyblok-kotlin/content-api-client/com.storyblok.cdn.schema/-rich-text/-document/index.html) in this case).
-   `FeaturedArticles.articles` is defined as `List<Story<Article>>` which will cause the [referenced](https://www.storyblok.com/docs/concepts/references) stories to be [resolved](https://www.storyblok.com/docs/concepts/references#resolve-relations-in-api-requests) and returned. To receive only the UUIDs of the linked stories change the type of the field to `List<Uuid>`.

> [!WARNING]
> The optional `resolveLevel` parameter of the [`story()`](https://storyblok.github.io/storyblok-kotlin/content-api-client/com.storyblok.cdn/-storyblok-client/story.html) function controls how deeply nested story references are resolved and defaults to one level deep. The API also limits the number of stories that can be resolved in a single request. Learn more in the [Content Delivery API documentation](/docs/api/content-delivery/v2/stories/retrieve-a-single-story) and the [References developer concept](/docs/concepts/references).
> 
> References that cannot be resolved can be handled in two ways:
> 
> -   Make the field nullable (for example: `Story<Article>?`). Unresolved references are then set to `null`.
> -   Change the field type from [`Story<T>`](https://storyblok.github.io/storyblok-kotlin/content-api-client/com.storyblok.cdn.schema/-story/index.html) to [`Uuid`](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.uuid/-uuid/) and resolve the stories in [subsequent requests](https://www.storyblok.com/docs/concepts/references#fetch-references-manually).

Next, add composables for these blocks inside the [`blockProvider`](https://storyblok.github.io/storyblok-kotlin/storyblok-material3/com.storyblok.compose.provider/block-provider.html) in `MainActivity.kt`.

app/src/main/java/com/example/myapplication/MainActivity.kt

```kotlin
…
import androidx.navigation3.ui.NavDisplay
import androidx.navigation3.runtime.entryProvider
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.material3.TextButton

  …
    blockProvider = blockProvider(
      fallback = { it, _ -> Text("Unknown block type '${it.component}'") }
     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())
       }
     }
    }
  …
```

Composables for `FeaturedArticles` and `Article` are registered alongside the existing blocks:

-   The `block<FeaturedArticles>` composable renders links to the featured articles, which on click construct a [`StoryKey`](/docs/guides/android/dynamic-routing#create-a-story-navigation-key) with the article `Story` instance for immediate display of the selected article.
-   The `block<Article>` composable invokes [`RichText()`](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose/-block-scope/-rich-text.html) to render the article’s rich text content.

> [!TIP]
> The [`storyblok-material3`](https://storyblok.github.io/storyblok-kotlin/storyblok-material3/index.html) library provides default composables for all rich text [node types](https://storyblok.github.io/storyblok-kotlin/content-api-client/com.storyblok.cdn.schema/-rich-text/index.html) using Material 3 theming and components. You can [override](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose.provider/-block-provider-scope/rich-text.html) the composable for any rich text node or [replace](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose.provider/block-provider-without-rich-text.html) the library entirely with an [alternative](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose.provider/-block-provider-scope/default-rich-text.html) implementation.

Also `storyLinkListener` is set in the [`blockProvider`](https://storyblok.github.io/storyblok-kotlin/storyblok-material3/com.storyblok.compose.provider/block-provider.html) to handle any story links present in the rich text content.

Run the app and see the home story with the links to the featured articles being rendered. Tapping any link navigates to the article.

## Related resources

[Concept: Fields](/docs/concepts/fields)

[Concept: References](/docs/concepts/references)

[Material 3 Rich Text Provider](https://storyblok.github.io/storyblok-kotlin/storyblok-material3/index.html)

## Pagination

-   [Previous: Dynamic Navigation in Android](/docs/guides/android/dynamic-routing)
-   [Next: Internationalization in Android](/docs/guides/android/internationalization)
