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

# Content Modeling in Vue

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
-   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/ArticleOverview.vue` file to get all stories from this new content type.

src/ArticleOverview.vue

```html
<script setup>
import { useStoryblokApi } from '@storyblok/vue';

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

<template>
  <h1>Article Overview</h1>
  <ul>
    <li v-for="article in articles" :key="article.uuid">
      <RouterLink :to="article.full_slug">
        {{ article.content.title }}
      </RouterLink>
    </li>
  </ul>
</template>
```

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`.

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

Add this to your router configuration.

src/main.js

```javascript
import { createApp } from 'vue';
import { StoryblokVue, apiPlugin } from '@storyblok/vue';
import { createWebHistory, createRouter } from 'vue-router'
import App from './App.vue';
import Page from './components/Page.vue';
import Article from './components/Article.vue';
import Teaser from './components/Teaser.vue';
import Grid from './components/Grid.vue';
import Feature from './components/Feature.vue';
import PageView from './PageView.vue'
import ArticleView from './ArticleView.vue'
import ArticleOverview from './ArticleOverview.vue';

const routes = [
  { path: '/', component: PageView },
  { path: '/:slug', component: PageView },
  { path: '/articles/:slug', component: ArticleView },
  { path: '/article-overview', component: ArticleOverview },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
})

const app = createApp(App);

app.use(StoryblokVue, {
  accessToken: import.meta.env.VITE_STORYBLOK_DELIVERY_API_TOKEN,
  use: [apiPlugin],
});

app.component('Page', Page);
app.component('Article', Article);
app.component('Teaser', Teaser);
app.component('Grid', Grid);
app.component('Feature', Feature);
app
  .use(router)
  .mount('#app');
```

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

## Create the article block

Add a new `src/components/Article.vue` component to render the new article content type.

src/components/Article.vue

```html
<script setup>
defineProps({ blok: Object });
</script>

<template>
  <div v-editable="blok">
    <h1>{{ blok.title }}</h1>
    <StoryblokRichText :doc="blok.content" />
  </div>
</template>
```

To render rich text fields, the `StoryblokRichText` component provided by the `@storyblok/vue` module is used.

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

Create an `ArticleView.vue` file to render this content type.

src/ArticleView.vue

```html
<script setup>
import { useStoryblok } from '@storyblok/vue';
import { useRoute } from 'vue-router'

const slug = useRoute().params.slug

const { data } = await useStoryblok(`articles/${slug}`, {
  version: 'draft',
});
const { story } = data;
</script>

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>
```

Update your main file to handle the new routes.

src/main.js

```javascript
import { createApp } from 'vue';
import { StoryblokVue, apiPlugin } from '@storyblok/vue';
import { createWebHistory, createRouter } from 'vue-router'
import App from './App.vue';
import Page from './components/Page.vue';
import Article from './components/Article.vue';
import Teaser from './components/Teaser.vue';
import Grid from './components/Grid.vue';
import Feature from './components/Feature.vue';
import PageView from './PageView.vue'
import ArticleView from './ArticleView.vue'

const routes = [
  { path: '/', component: PageView },
  { path: '/:slug', component: PageView },
  { path: '/articles/:slug', component: ArticleView },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
})

const app = createApp(App);

app.use(StoryblokVue, {
  accessToken: import.meta.env.VITE_STORYBLOK_DELIVERY_API_TOKEN,
  use: [apiPlugin],
});

app.component('Page', Page);
app.component('Article', Article);
app.component('Teaser', Teaser);
app.component('Grid', Grid);
app.component('Feature', Feature);
app
  .use(router)
  .mount('#app');
```

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

## Handle referenced stories

In the `PageView.vue` file, set the `resolve_relations` parameter to get the full object response of referenced stories.

src/PageView.vue

```html
<script setup>
import { useStoryblokApi, useStoryblokBridge } from '@storyblok/vue';
import { useRoute } from 'vue-router'

const slug = useRoute().params.slug

const storyblokApi = useStoryblokApi();
const { data } = await storyblokApi.get(`cdn/stories/${slug ? slug : 'home'}`, {
  version: 'draft',
  resolve_relations: 'featured-articles.articles'
});
const { story } = data;
useStoryblokBridge(story.id)
</script>

<template>
  <StoryblokComponent v-if="story" :blok="story.content" />
</template>
```

> [!NOTE]
> Learn more in the [references concept](https://www.storyblok.com/docs/concepts/references) documentation.

Next, create a new `src/components/FeaturedArticles.vue` component.

src/components/FeaturedArticles.vue

```html
<script setup>
defineProps({ blok: Object});
</script>

<template>
  <section v-editable="blok">
    <h2>Featured Articles</h2>
    <ul>
      <li v-for="article in blok.featured_articles" :key="article.uuid">
        <RouterLink :to="article.full_slug">
          {{ article.content.title }}
        </RouterLink>
      </li>
    </ul>
  </section>
</template>
```

src/main.js

```javascript
import { createApp } from 'vue';
import { StoryblokVue, apiPlugin } from '@storyblok/vue';
import { createWebHistory, createRouter } from 'vue-router'
import App from './App.vue';
import Page from './components/Page.vue';
import Article from './components/Article.vue';
import Teaser from './components/Teaser.vue';
import Grid from './components/Grid.vue';
import Feature from './components/Feature.vue';
import FeaturedArticles from './components/FeaturedArticles.vue';
import PageView from './PageView.vue'
import ArticleView from './ArticleView.vue'

const routes = [
  { path: '/', component: PageView },
  { path: '/:slug', component: PageView },
  { path: '/articles/:slug', component: ArticleView },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
})

const app = createApp(App);

app.use(StoryblokVue, {
  accessToken: import.meta.env.VITE_STORYBLOK_DELIVERY_API_TOKEN,
  use: [apiPlugin],
});

app.component('Page', Page);
app.component('Article', Article);
app.component('Teaser', Teaser);
app.component('Grid', Grid);
app.component('Feature', Feature);
app.component('FeaturedArticles', FeaturedArticles);
app
  .use(router)
  .mount('#app');
```

Now, this component will render links to the featured articles in the home page of your 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 Vue](/docs/guides/vue/dynamic-routing)
