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.
In the existing space, create the following blocks:
- An
articlecontent type block with the following fields:title: Textcontent: Rich text
- An
article-overviewcontent type block with the following field:title: Text
- A
featured-articlesnestable block with the following field:articles: References
Next, create an Articles folder, open it, and create the following stories:
- A few stories that use the
articlecontent type. - An article overview story with an
article-overviewcontent 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
Section titled “Fetch and list all articles”To get all stories from this new content type, create a new src/components/ArticleOverview.astro file.
---import { apiClient } from "../storyblok";const { block, editable } = Astro.props;
const { data } = await apiClient.stories.list({ query: { version: "draft", starts_with: "articles", content_type: "article", },});
const articles = data?.stories ?? [];---
<div {...editable}> <h1>{block.title}</h1> <ul> { articles.map((article) => ( <li> <a href={`/${article.full_slug}`}>{article.content.title}</a> </li> )) } </ul></div>The starts_with parameter limits the results to stories in the Articles folder, and the content_type parameter limits them to stories of the article content type.
Register this block in the src/storyblok.ts file.
import { createApiClient } from "@storyblok/api-client";import { defineStoryblokBlocks } from "@storyblok/astro";import Page from "./components/Page.astro";import Grid from "./components/Grid.astro";import Feature from "./components/Feature.astro";import Teaser from "./components/Teaser.astro";import ArticleOverview from "./components/ArticleOverview.astro";
export const apiClient = createApiClient({ accessToken: import.meta.env.STORYBLOK_DELIVERY_API_TOKEN, region: "eu",});
export const { StoryblokBlock } = defineStoryblokBlocks(() => ({ components: { page: Page, grid: Grid, feature: Feature, teaser: Teaser, "article-overview": ArticleOverview, },}));Now, the article overview page shows a list of links to all articles.
Create the article block
Section titled “Create the article block”Add a new Article.astro component to render the new article content type.
---import { StoryblokRichText } from "../storyblok";const { block, editable } = Astro.props;---
<article {...editable}> <h2>{block.title}</h2> <StoryblokRichText document={block.content} /></article>To render rich text fields, use the StoryblokRichText component returned by defineStoryblokBlocks. It renders blocks embedded in the rich text field with the same registered components as StoryblokBlock.
Register this block in the src/storyblok.ts file, and export StoryblokRichText.
import { createApiClient } from "@storyblok/api-client";import { defineStoryblokBlocks } from "@storyblok/astro";import Page from "./components/Page.astro";import Grid from "./components/Grid.astro";import Feature from "./components/Feature.astro";import Teaser from "./components/Teaser.astro";import ArticleOverview from "./components/ArticleOverview.astro";import Article from "./components/Article.astro";
export const apiClient = createApiClient({ accessToken: import.meta.env.STORYBLOK_DELIVERY_API_TOKEN, region: "eu",});
export const { StoryblokBlock } = defineStoryblokBlocks(() => ({export const { StoryblokBlock, StoryblokRichText } = defineStoryblokBlocks(() => ({ components: { page: Page, grid: Grid, feature: Feature, teaser: Teaser, "article-overview": ArticleOverview, article: Article, },}));When you select a link on the article overview page, the article page renders correctly.
Handle referenced stories
Section titled “Handle referenced stories”In the src/pages/[...slug].astro file, set the resolve_relations parameter to get the full object response of referenced stories. Pass the same relation to the bridgeOptions property of StoryblokPreview to resolve it during live preview.
---import { getPayload, StoryblokPreview } from "@storyblok/astro";import { apiClient, StoryblokBlock } from "../storyblok";import Layout from "../layouts/Layout.astro";
const { slug } = Astro.params;
const payload = await getPayload({ locals: Astro.locals });
const story = payload.story ?? ( await apiClient.stories.get(slug || "home", { query: { version: "draft", resolve_relations: "featured-articles.articles", }, }) ).data?.story;
if (!story) { return new Response(null, { status: 404 });}---
<Layout> <StoryblokBlock block={story.content} /> <StoryblokPreview liveUpdate /> <StoryblokPreview liveUpdate bridgeOptions={{ resolveRelations: ["featured-articles.articles"] }} /></Layout>In the src/storyblok.ts file, set the inlineRelations option to replace the references in the story content with the resolved stories.
export const apiClient = createApiClient({ accessToken: import.meta.env.STORYBLOK_DELIVERY_API_TOKEN, region: "eu", inlineRelations: true,});Next, create a new src/components/FeaturedArticles.astro component.
---import type { Story } from "@storyblok/api-client";const { block, editable } = Astro.props;---
<section {...editable}> <h2>Featured articles</h2> <ul> { block.articles?.map((article: Story) => ( <li> <a href={`/${article.full_slug}`}>{article.content.title}</a> </li> )) } </ul></section>Register this block in the src/storyblok.ts file.
import { createApiClient } from "@storyblok/api-client";import { defineStoryblokBlocks } from "@storyblok/astro";import Page from "./components/Page.astro";import Grid from "./components/Grid.astro";import Feature from "./components/Feature.astro";import Teaser from "./components/Teaser.astro";import ArticleOverview from "./components/ArticleOverview.astro";import Article from "./components/Article.astro";import FeaturedArticles from "./components/FeaturedArticles.astro";
export const apiClient = createApiClient({ accessToken: import.meta.env.STORYBLOK_DELIVERY_API_TOKEN, region: "eu", inlineRelations: true,});
export const { StoryblokBlock, StoryblokRichText } = defineStoryblokBlocks(() => ({ components: { page: Page, grid: Grid, feature: Feature, teaser: Teaser, "article-overview": ArticleOverview, article: Article, "featured-articles": FeaturedArticles, },}));Now, this component renders links to the featured articles on the home page of the project.
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.
- Need help?Contact support (opens in a new window)
- Something isn’t right?Check system status (opens in a new window)
Get in touch with the Storyblok community