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

# Content Modeling in Astro

Learn how to handle different content types 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
-   An `article-overview` content type block with the following field:
    -   `title`: Text
-   A `featured-articles` nestable block with the following field:
    -   `articles`: References

  

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

Next, create an `Articles` folder, open it, and create the following stories:

-   A few stories that use the `article` content type.
-   An article overview story with a `article-overview` content type. Select the option **Define as root for the folder**.

Finally, add the `featured-articles` block to the `body` field of the **Home** story, and select articles to feature.

## Fetch and list all articles

Create a new `src/storyblok/ArticleOverview.astro` file to get all stories from this new content type.

src/storyblok/ArticleOverview.astro

```astro
---
import { useStoryblokApi } from '@storyblok/astro';

const storyblokApi = useStoryblokApi();
const articles = await storyblokApi.getAll('cdn/stories', {
  version: 'draft',
  starts_with: 'articles',
  content_type: 'article',
});
---

<div>
  <h1>Articles overview</h1>
  <ul>
  {
    articles.map((article) => (
      <li>
        <a href={article.slug}>{article.content.title}</a>
      </li>
    ))
  }
  </ul>
</div>
```

Using the `starts_with` parameter, only stories from the “Articles” folder are fetched. Using the `content_type` parameter, the results are restricted to stories of the content type `article`.

> [!TIP]
> Learn more about parameters and filter queries in the [Content Delivery API documentation](https://www.storyblok.com/docs/api/content-delivery/v2).

Register this block in the Astro configuration file.

astro.config.mjs

```javascript
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const { STORYBLOK_DELIVERY_API_TOKEN } = loadEnv(import.meta.env.MODE, process.cwd(), "");
import mkcert from 'vite-plugin-mkcert';

export default defineConfig({
  integrations: [
    storyblok({
      accessToken: STORYBLOK_DELIVERY_API_TOKEN,
      components: {
        page: 'storyblok/Page',
        grid: 'storyblok/Grid',
        feature: 'storyblok/Feature',
        teaser: 'storyblok/Teaser',
        ['article-overview']: 'storyblok/ArticleOverview',
      },
    }),
  ],
  output: 'server',
  vite: {
    plugins: [mkcert()],
  },
});
```

Now, the article overview page shows a list of links to all articles.

## Create the articles block

Add a new `Article.astro` component to render the new article content type.

src/storyblok/Article.astro

```astro
---
import { storyblokEditable, renderRichText } from '@storyblok/astro';

const { blok } = Astro.props;
const renderedRichText = renderRichText(blok.content);
---

<article {...storyblokEditable(blok)}>
  <h2>{blok.title}</h2>
  <Fragment set:html={renderedRichText} />
</article>
```

To render rich text fields, the `renderRichText` function provided by the `@storyblok/astro` module is used.

> [!NOTE]
> Learn more about handling rich text in Storyblok in the [fields concept](/docs/concepts/fields) and the [@storyblok/richtext reference](https://www.storyblok.com/docs/libraries/js/richtext).

Register this block in the Astro configuration file.

astro.config.mjs

```javascript
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const { STORYBLOK_DELIVERY_API_TOKEN } = loadEnv(import.meta.env.MODE, process.cwd(), "");
import mkcert from 'vite-plugin-mkcert';

export default defineConfig({
  integrations: [
    storyblok({
      accessToken: STORYBLOK_DELIVERY_API_TOKEN,
      components: {
        page: 'storyblok/Page',
        grid: 'storyblok/Grid',
        feature: 'storyblok/Feature',
        teaser: 'storyblok/Teaser',
        ['article-overview']: 'storyblok/ArticleOverview',
        article: 'storyblok/Article',
      },
    }),
  ],
  output: 'server',
  vite: {
    plugins: [mkcert()],
  },
});
```

When clicking on links present in the article overview page, an article page renders correctly.

## Handle referenced stories

In the `src/pages/[...slug].astro` file, set the `resolve_relations` parameter to get the full object response of referenced stories.

src/pages/\[...slug\].astro

```astro
---
import { useStoryblokApi } from "@storyblok/astro";
import StoryblokComponent from "@storyblok/astro/StoryblokComponent.astro";
import Layout from "../layouts/Layout.astro";

const { slug } = Astro.params;

const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get(`cdn/stories/${slug || "home"}`, {
  version: "draft",
  resolve_relations: 'featured-articles.articles',
});

const story = data.story;
---

<Layout>
  <StoryblokComponent blok={story.content} />
</Layout>
```

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

Next, create a new `src/storyblok/FeaturedArticles.astro` component.

src/storyblok/FeaturedArticles.astro

```astro
---
import { storyblokEditable } from '@storyblok/astro';

const { blok } = Astro.props;
const articles = blok.articles;
---

<section {...storyblokEditable(blok)}>
  <h2>Featured articles</h2>
  <ul>
  {
    articles.map((article) => (
      <li>
        <a href={article.full_slug}>{article.content.title}</a>
      </li>
    ))
  }
  </ul>
</section>
```

Register this block in the Astro configuration file.

astro.config.mjs

```javascript
import { defineConfig } from 'astro/config';
import { storyblok } from '@storyblok/astro';
import { loadEnv } from 'vite';
const { STORYBLOK_DELIVERY_API_TOKEN } = loadEnv(import.meta.env.MODE, process.cwd(), "");
import mkcert from 'vite-plugin-mkcert';

export default defineConfig({
  integrations: [
    storyblok({
      accessToken: STORYBLOK_DELIVERY_API_TOKEN,

      // Only required when using the live preview functionality:
      // livePreview: true,
      // bridge: {
          // resolveRelations: ['featured-articles.articles'],
        // },

      components: {
        page: 'storyblok/Page',
        grid: 'storyblok/Grid',
        feature: 'storyblok/Feature',
        teaser: 'storyblok/Teaser',
        ['article-overview']: 'storyblok/ArticleOverview',
        article: 'storyblok/Article',
        ['featured-articles']: 'storyblok/FeaturedArticles',
      },
    }),
  ],
  output: 'server',
  vite: {
    plugins: [mkcert()],
  },
});
```

Now, this component will render links to the featured articles in the home page of the project.

## Related resources

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

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

[@storyblok/richtext Package Reference](https://www.storyblok.com/docs/libraries/js/richtext)

  

## Pagination

-   [Previous: Dynamic Routing in Astro](/docs/guides/astro/dynamic-routing)
-   [Next: Internationalization in Astro](/docs/guides/astro/internationalization)
