Skip to content

Content Modeling in Next.js

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

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

Next, create a 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.

Create a new components/ArticleOverview.jsx file to get all stories from this new content type.

components/ArticleOverview.jsx
import { getStoryblokApi } from '@/lib/storyblok';
import Link from 'next/link';
export default async function ArticleOverview() {
const storyblokApi = getStoryblokApi();
let articles = await storyblokApi.getAll('cdn/stories', {
version: 'draft',
starts_with: 'articles',
content_type: 'article',
});
return (
<div>
<h1>Articles</h1>
<ul>
{articles.map((article) => (
<li key={article.uuid}>
<Link href={`/${article.full_slug}`}>{article.name}</Link>
</li>
))}
</ul>
</div>
);
}

The starts_with parameter is being used to fetch stories from the “Articles” folder. The content_type parameter restricts the results to stories of the content type article, ensuring that the article_overview is excluded.

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

Add a new components/Article.jsx component to render the new article content type.

components/Article.jsx
import { storyblokEditable, StoryblokServerRichText } from '@storyblok/react/rsc';
const Article = ({ blok }) => {
return (
<div className="article" {...storyblokEditable(blok)}>
<h1>{blok.title}</h1>
<p>
<StoryblokServerRichText doc={blok.content} />
</p>
</div>
);
};
export default Article;

To render rich text fields, use the StoryblokServerRichText component provided by the @storyblok/react/rsc module.

For client components only, import the StoryblokRichText component from @storyblok/react.

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

In the src/app/[...slug]/page.js data file, use the resolve_relations parameter to receive the complete story object for referenced stories. Also add it as the bridgeOptions argument to StoryblokStory.

src/app/[...slug]/page.js
import { StoryblokStory } from '@storyblok/react/rsc';
import { getStoryblokApi } from '@/lib/storyblok';
export default async function Page({ params }) {
const { slug } = await params;
const fullSlug = slug ? slug.join('/') : 'home';
const storyblokApi = getStoryblokApi();
let { data } = await storyblokApi.get(`cdn/stories/${fullSlug}`, {
version: 'draft',
resolve_relations: 'featured-articles.articles',
});
return (
<div>
<StoryblokStory
story={data.story}
bridgeOptions={{
resolveRelations: 'featured-articles.articles',
}}
/>
</div>
);
}

Next, create a new src/components/FeaturedArticles.jsx component.

src/components/FeaturedArticles.jsx
import { storyblokEditable } from '@storyblok/react';
export default function FeaturedArticles({ blok }) {
return (
<section {...storyblokEditable(blok)} className="featured-articles">
<h2>Featured Articles</h2>
<ul>
{blok.articles.map((article) => (
<li key={article.uuid}>
<a href={`/${article.full_slug}`}>{article.name}</a>
</li>
))}
</ul>
</section>
);
}

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


Was this page helpful?

What went wrong?

This site uses reCAPTCHA and Google's Privacy Policy (opens in a new window) . Terms of Service (opens in a new window) apply.