Skip to content

Integrate Next.js with Storyblok

Use Storyblok to manage the content of your website built with the Next.js app router.

Create a new Next.js project by following the Installation page from its official documentation.

If you already have a Storyblok account, go to app.storyblok.com or log in with GitHub to continue.

Create a new blank space (opens in a new window) to follow the tutorial from scratch, or start from the core blueprint (opens in a new window).

In the terminal, cd into the Next.js project and install the @storyblok/react package.

Terminal window
npm install @storyblok/react@latest

Create a new file under src/lib/storyblok.js and initialize the Storyblok module:

src/lib/storyblok.js
import { apiPlugin, storyblokInit } from '@storyblok/react/rsc';
export const getStoryblokApi = storyblokInit({
accessToken: process.env.NEXT_PUBLIC_STORYBLOK_CONTENT_API_ACCESS_TOKEN,
use: [apiPlugin],
apiOptions: {
region: 'eu',
},
});

In the root of your project, create a .env file with the access token from your space.

.env
NEXT_PUBLIC_STORYBLOK_CONTENT_API_ACCESS_TOKEN=<YOUR-ACCESS-TOKEN>

Add a src/components/StoryblokProvider.jsx file with the following content:

src/components/StoryblokProvider.jsx
"use client";
import { getStoryblokApi } from "@/lib/storyblok";
export default function StoryblokProvider({ children }) {
getStoryblokApi();
return children;
}

In the existing src/app/layout.js file, import the StoryblokProvider component and wrap the RootLayout one.

src/app/layout.js
import StoryblokProvider from "@/components/StoryblokProvider";
export default function RootLayout({ children }) {
return (
<StoryblokProvider>
<html lang="en">
<body>
{children}
</body>
</html>
</StoryblokProvider>
);
}

The StoryblokProvider component will make features like fetching, component registration, and bridge available across your project.

Replace the code in src/app/page.js with the following.

src/app/page.js
import { getStoryblokApi } from '@/lib/storyblok';
import { StoryblokStory } from '@storyblok/react/rsc';
export default async function Home() {
const { data } = await fetchData();
return (
<div className="page">
<StoryblokStory story={data.story} />
</div>
);
}
export async function fetchData() {
const storyblokApi = getStoryblokApi();
return await storyblokApi.get(`cdn/stories/home`, { version: 'draft' });
}

The StoryblokStory dynamically renders content type and nestable blocks. In this case, it looks for the content type block of the home story.

Create a src/components/Page.jsx component to render all stories of the page content type, such as the home story.

src/components/Page.jsx
import {
storyblokEditable,
StoryblokServerComponent,
} from '@storyblok/react/rsc';
export default function Page({ blok }) {
return (
<main>
{blok.body?.map((nestedBlok) => (
<StoryblokServerComponent blok={nestedBlok} key={nestedBlok._uid} />
))}
</main>
);
}

Using StoryblokServerComponent iterate through the body field and render the blocks in it.

Stories might contain a body or a similar blocks field which consists of an array with several blocks of custom types (e.g., Feature, Teaser, Grid) in it.

Create the code for these components as follows.

src/components/Feature.jsx
export default function Feature({ blok }) {
return (
<div className="feature">
<span>{blok.name}</span>
</div>
);
}
src/components/Teaser.jsx
export default function Teaser({ blok }) {
return (
<div className="teaser">
<h2>{blok.headline}</h2>
</div>
);
}
src/components/Grid.jsx
import { StoryblokServerComponent } from '@storyblok/react/rsc'
export default function Grid({ blok }){
return (
<div className="grid">
{blok.columns?.map((nestedBlok) => (
<StoryblokServerComponent blok={nestedBlok} key={nestedBlok._uid} />
))}
</div>
);
};

Similar to Page.jsx, Grid.jsx iterates over the columns block field.

Add these components to the src/lib/storyblok.js file.

src/lib/storyblok.js
import Page from "@/components/Page";
import Feature from "@/components/Feature";
import Grid from "@/components/Grid";
import Teaser from "@/components/Teaser";
import { apiPlugin, storyblokInit } from "@storyblok/react/rsc";
export const getStoryblokApi = storyblokInit({
accessToken: process.env.STORYBLOK_DELIVERY_API_ACCESS_TOKEN
use: [apiPlugin],
components: {
page: Page,
feature: Feature,
grid: Grid,
teaser: Teaser
},
apiOptions: {
endpoint: "https://api.storyblok.com",
},
});

Run the server and visit the site in your browser.

Terminal window
npm run dev