Add a Headless CMS to Next.js 13 in 5 minutes
Storyblok is the first headless CMS that works for developers & marketers alike.
In this short tutorial, we will explore how to integrate Storyblok into a Next.js 13 application with app router and enable the live preview in the Visual Editor. We will use Storyblok's React SDK to load our data using the Storyblok API. We will see two different approaches, one of which uses the server-side components partially to enable the live editing experience and one that uses the server-side components fully.
Wait no more, and get started with Next.js 13 Boilerplate right now!
If you use Next.js 14+ with App Router, we released a new version of our React SDK fully supporting Server Components, with live editor enabled for developers.
The integration steps to use the latest version are slightly different, you can find a detailed guide in the README of our repo.
Environment Setup
Requirements
To follow this tutorial, the following requirements need to be fulfilled:
- Basic understanding of JavaScript, React, and Next.js
- Node.js LTS version
- An account in the Storyblok App
The project in this tutorial are developed using the following versions:
@storyblok/react@2.1.0
Please keep in mind that these versions may be slightly behind the latest ones.
Create a Next.js project
Following the Next.js Offical Docs, we can easily create a new Next.js project with the following command:
Then, let's start the development server:
Open your browser at http://localhost:3000. You should see the following screen:
Create a new space in the Storyblok app by choosing the Create space {1} option. Pick a name for it {2}. Optionally, you can choose between different server locations for your space {3} (if you choose the United States or China, please be mindful of the required API parameter explained hereinafter).
Shortly afterward, a Storyblok space with sample content has been created for you. Let’s open the Home story by first clicking on Content {1} and then on Home {2}:
Now you’ll see the default screen and the Visual Editor:
If you don't know how to setup an HTTPS proxy, you can read this guide to configure it on macOS, or this guide if you are a Windows user.
In order to actually see the Next.js project in the Visual Editor, we’ll have to define the default environment URL. Let’s do that by going to Settings > Visual Editor {1} and setting the Location field to https://localhost:3010/ {2}:
Now, if you go back to the Home story, you won’t see your Next app there just yet.
Setting the Real Path
In the Entry configuration {1}, We need to set the Real Path {2} to /
. We want to display the story with the slug home
under our base path /
and not /home
. Once you set the preview URL and the real path, you should be able to see your development server inside Storyblok showing the name of the story Home.
Connecting to Storyblok
Let's now connect Storyblok to our Next.js project.
First of all, let's install our official React SDK. This package allows us to interact with the Storyblok API and will help us to enable the real-time editing experience:
To initialize Storyblok in your Next.js Project, go to the layout.js
inside the app
directory of the project and add the storyblokInit
function as follows:
The SDK has a special module for App Router. Always import from @storyblok/react/rsc
while using Server Components.
The implementation details differ slightly so be sure to check the README of our repo.
In the Storyblok app, retrieve your Preview token {3} from your Space Settings {1} under Access Tokens {2}. Add the token as the accessToken
directly, or from a .env
file.
If you want to use an environment variable, you should follow this official Next.js tutorial. You should have a next.config.js
file in your project, and add the env
config storyblokApiToken: process.env.STORYBLOK_API_TOKEN
, in order to set accessToken: process.env.storyblokApiToken
in your storyblokInit
function.
Setting the correct region
Depending on whether your space was created in the EU, the US, Australia, Canada, or China, you may need to set the region
parameter of the API accordingly:
eu
(default): For spaces created in the EUus
: For spaces created in the USap
: For spaces created in Australiaca
: For spaces created in Canadacn
: For spaces created in China
Here's an example for a space created in the US:
Note: For spaces created in any region other than the EU, the region parameter must be specified.
This storyblokInit
function will take care of two things: Initialize the connection with Storyblok (for enabling the Visual Editor) and provide an API client that we can use to retrieve content from the platform, to be used in our application.
If you use Next.js 14+ with App Router, the next steps are DEPRECATED: be sure to check the guide in the README of our repo.
With Live Editing Support
If you're in hurry, you can explore or fork the main branch of Next.js 13 Storyblok Boilerplate.
As all of the components are by default on the server side while using the new app directory, it limits the components' reactivity which is required to see real-time updates. In this approach, we create a wrapper to load the components on the client side. This allows us to take full advantage of the Live Editing, but the use of Server Side Components is partial. Let's create this wrapper.
Inside the components
folder, create a new file and name it StoryblokProvider.js
. Add the following code to it:
We are tagging this as a client component and we are reinitializing the Storyblok connection here again inside this file as we need to have it on the client as well. The initialization inside the layout file which we did previously will help us to fetch the data from Storyblok.
Now we need to use this wrapper in the layout. The layout.js
file should have the following code:
We are using the getStoryblokApi
object which is imported from @storyblok/react/rsc
to fetch the content of the home
story from Storyblok inside the fetchData
function. You should be able to see the name of the story (Home) while viewing the website.
Rendering Dynamic Components in the Next.js App
The core idea of using Storyblok is the following:
- Content managers (even if it’s only yourself) can create pages (or stories) composed of different components (or blocks)
- Developers receive the page in the JSON format by using the Storyblok API and can render components accordingly (this is what we want to accomplish in our Next app). We already did this in our previous step.
When you create a new space from scratch, Storyblok automatically creates four default components for you:
page
: Content type blockgrid
: Nested blockfeature
: Nested blockteaser
: Nested block
You can find all of these in the Components section of your space.
Understand the difference between the nestable components and content type in our Structures of Content tutorial.
By using storyblokEditable
with any component, we can make them clickable in the Storyblok Visual Editor, and we can edit their properties in real time.
To load the right content in Next.js, we will need a dynamic element that can resolve the component names we get from Storyblok API to the actual components in our Next.js application. For this purpose, we use the StoryblokComponent
feature included in the SDK. You can see how it works in the Page
and Grid
components, where we have the body
and columns
properties that can load any type of component.
Finally, we need to configure the components identified by StoryblokComponent
, and link them to their representation in the Storyblok space. To do that, let's go back to StroyblokProvider.js
and add a new parameter to storyblokInit
call:
We are now left with just one thing, which is to change the page.js
file present inside the app directory in order to render the content we get from the API and enable live editing. So instead of displaying the story's name, let's render the components.
In order to do this, we need to import StoryblokStory
component from "@storyblok/react/story"
. It takes a prop named story
where you need to pass the story coming from the API. This component uses a hook behind the scenes to keep the state of the story for enabling live editing and renders everything with the StoryblokComponent
itself.
The page.js
file should now look like this:
And that is it! Now if you open your website, you will see that it contains the content coming from the components we have inside the story, that is the Teaser
and Grid
components. Inside the Visual Editor, you will also be able to see the dotted lines which help in identifying the components. If you click over on any place inside the visual editor, you will see that the schema of that component opens up on the right-hand side.
Now you can start live editing by just clicking and filling in fields with your content. Play with changing the teaser headline or re-arranging the features and see the magic happen!
Optional: Changing Styles
Let’s add TailwindCSS to our project for the ease of adding styles. You can refer to this guide for adding TailwindCSS to Next.js.
After adding TailwindCSS, let’s add a couple of classes to the Teaser, Grid, and Page components.
After adding the Tailwind classes to our components and removing the story name from page.js
file, we should see the following:
Full React Server Components
If you're in hurry, you can clone or explore the full-server-side branch of Next.js 13 Storyblok Bolierplate.
In the previous approach, we saw how to enable live editing with the server components. This makes the use of server components partial. In this approach, we will see how to use the full potential of server components by keeping everything server-side.
This approach has a limitation. Real-time editing won't work if all the components are rendered on the server. Although, you can see the changes applied in the Visual Editor whenever you save or publish the changes applied to the story.
After initializing Storyblok by installing @storyblok/react
and using the storyblokInit
function inside the layout.js
file, import StoryblokBridgeProvider
and replace the code inside the file with the following:
The StoryblokBridgeLoader
loads the bridge on the client behind the scenes. Though live editing will not work, it will load the bridge so that inside the Visual Editor you're still able to click the components. Along with this, it will also register events so that the Visual Editor reloads when you click Save or Publish. You can pass bridge options through a prop named options
.
Fetching Data and Rendering Components
Most of the part here remains similar to that we have in the previous approach. The fetching of the data and creation of the components remains the same.
You can refer to the previous section understand the setup of data fetching inside the page.js
file and creating components. Once the components are created, we need to register them inside storyblokInit
function. Let's do that inside the layout.js
file.
We are left with just one thing now, that is to render these components dynamically after fetching the data inside the page.js
file. We can do that by using the StoryblokComponent
and passing the story's content.
And that's it. You should be able to see everything inside the Visual Editor now. Click on any component to open the schema on the right-hand side and change the content. Save or Publish to see the changes.
Wrapping up
Congratulations! We learned how to integrate Storyblok into a Next.js 13 project with app router. We went through two approaches, one with live-editing support where the use of server components is partial, and another, with full use of server-side components which limits the live editing. We also saw how to manage and consume content using the Storyblok API.
Resource | Link |
---|---|
Next.js 13 Storyblok Boilerplate | Next.js 13 Storyblok Boilerplate |
Next.js Technology Hub | Storyblok Next.js Technology Hub |
React Technology Hub | Storyblok React Technology Hub |
Storyblok React SDK | storyblok/storyblok-react |
Storyblok Next.js Ultimate Tutorial with Pages Router | https://www.storyblok.com/tp/nextjs-headless-cms-ultimate-tutorial |
Next.js Documentation | Next.js Docs |