Add a GraphQL enabled Headless CMS to your Remix app with Storyblok
Storyblok is the first headless CMS that works for developers & marketers alike.
Remix is an increasingly popular server-side rendering framework built on solid web principles for React.
To take advantage of the speed and efficiency of this framework, GraphQL becomes an excellent option for interacting with external APIs such as a Headless CMS.
With the Storyblok GraphQL API, we can leverage the speed and efficiency of both Remix and GraphQL to build an efficient and dynamic application powered by Storyblok.
Storyblok GraphQL API
Here, we’ll briefly cover the Storyblok GraphQL API and highlight a few benefits over traditional REST. Storyblok provides a GraphQL API alongside the REST API, which is optimized for fast content delivery.
This is a read-only endpoint, meaning that changes to your content from your application will still have to be made via the Storyblok management API.
Also, since it’s a GraphQL endpoint, it offers several advantages, like automated documentation and strongly typed responses.
The GraphQL API can be accessed via the following endpoints:
- https://gapi.storyblok.com/v1/api and
- https://gapi-<region-code>.storyblok.com/v1/api e.g. https://gapi-us.storyblok.com/v1/api for a specific region.
The region code in the Storyblok endpoint is needed when making a CDN request for that specific region.
To gain access to your data via the API, you’ll need to send an API token in the Token header of your request. You can always learn now about the GraphQL API from the official docs.
In the next section, we’ll look at what we will build using GraphQL API and Remix.
What we’re building
We’ll be building a simple site with the following pages:
- The home page
- An articles page
- Individual article pages
Each page will have its content pulled from the Storyblok Headless CMS.
The site will be connected to the Storyblok Visual editor, and components in the pages will be rendered using the Storyblok SDK, which allows them to be editable in the Visual editor.
Prerequisites
To follow along with this article, we’ll need the following:
- A text editor (VS Code, for example)
- Basic knowledge of Remix
- Basic knowledge of GraphQL
- A recent Node.js version installed
- A Storyblok account, you can create an account if you haven't already.
Set up the Remix project
This section we will quickly set up a new Remix project with TailwindCSS for styling.
To make the process much easier, there’s a starter template setup with Apollo Client, Remix, and TailwindCSS available on GitHub.
Here is a fork of https://github.com/jgarrow/remix-apollo, a demonstration of using Apollo Client with Remix. It uses Apollo's recommended setup for SSR. This allows us to take advantage of Apollo Client's hooks and caching while still having requests done on the server.
- You can read more on how to set up Remix with Apollo Client on the official Apollo blog.
For now, we can navigate to a folder of choice and create a new Remix project based on the template by running this command in the terminal:
Then follow the prompts:
Or we can clone the project from GitHub and install
This installs the Remix project, TailwindCSS, and other packages, including @apollo/client
graphql for Apollo Client and @storyblok/react
for integrating with Storyblok.
Once the project has been created and installed, we can proceed. If we run:
We should have something like this:
Also, for this tutorial, we will set up our dev server with an HTTPS proxy.
The Storyblok visual editor requires us to use a secure connection with the application.
We'll be using port 3010, so the URL to access our website will end up being https://localhost:3010/.
- If you don’t know how to set up an HTTPS proxy on macOS, you can read this guide.
- If you’re on a Widows device, you can also find the guide for windows here.
Next, let's set up GraphL and Apollo client in our application.
Set up Storyblok space
To get started, navigate to our Storyblok dashboard, click on My Spaces {1} and create a new Space by entering the name of the space and server location.
Once the space has been created, let’s get an API token we’ll use to make requests from our application.
Since we’ve set up HTTPS
proxy using the guide earlier, we can enter the URL for our app to use the visual editor.
Once that’s done, navigate to Settings {1}, click on Access Tokens {2} and copy the token. We’ll use it to access our GraphQL API.
Back to the application project, create a new file - ./.env
With this token, we can set up authentication in our Apollo Client configuration to connect to our Storyblok GraphQL API.
To do that, open the ./app/entry.server.tsx
file and replace it with the following:
Here we set up HttpLink
with the URL to our Storyblok GraphQL endpoint as our URI.
Next, to fetch data, we need to set up authentication, which we did by configuring ApolloLink
to add the token
and version
to the operation headers.
Finally, in ApolloClient
, we set link: concat(authMiddleware, httpLink)
to use the authentication and GraphQL API.
Fetch data from Storyblok
Finally, to fetch data from Storyblok, we define a query and pass it to the useQuery()
function. You can see how to make a GraphQL query from the Storyblok API here from this guide
Storyblok’s GraphQL schema is generated from our content types. For each content type, Storyblok generates two fields:
- One for receiving a single item: [Humanized Name]Item e.g., PageItem
- And one for receiving multiple items: [Humanized Name]Items e.g., PageItems
If you have created a content type with the name Product
, you will have the fields ProductItem
and ProductItems
in GraphQL.
Below, we will query the home
content item and output the page name.
In ./app/routes/index.tsx
:
Now we should see something like this:
Also, to get a documented schema definition of your content type, we can use the GraphQL playground.
We have to exchange the token (YOUR_TOKEN
) with our Preview
token and open the link: http://gapi-browser.storyblok.com/?token=YOUR_TOKEN
Set up Storyblok Content structure
Now, we’ll walk through how to set up a basic content structure for our site in Storyblok.
Create a new story
Let’s create a new story for our site, Storyblok has set up a demo story already, but we’ll ignore that for now and create a new story.
Click on the Create new {1} button and the Story {2} option from the dropdown.
Now, we’ll give our new story a name. Let’s name it the Home page
, the slug is automatically generated.
Next: we have to modify the Entry Configuration {1} and set the Real Path {2} corresponding to the index route in our Remix application.
Once we’ve done that, you should see the page showing in the Visual editor.
Create Hero block
Now, we’ll create a new nestable block that will live in our home page story. This will contain the data for a <Hero /> component we’ll create later in our application.
To create a new block, click the Block Library button at the top of the page that opens up this modal.
Next, click on the New Block {1} button. On the side, we can enter the name of our component and its type, which should correspond to the following:
- “Hero” - Technical name
- Nestable block - Block type
Now, click the Add block {2} button to create the block so we can start editing.
Now, for our Hero block, let’s create a few fields. To create a new field, enter the field name {1} and select the field type by clicking on the field type icon {2} and save {3}.
Here’s the schema for our Hero block:
- “caption” - Text {1}
- “text” - Text {2}
- “image” - Asset {3}
It should look something like this:
Now that we’ve set up our Hero block, we can save it and add our content. Here’s mine below:
Create Articles folder
This folder will contain all our article stories.
To create a new folder, open the Browse content sidebar and click on the + button to select the Folder option from the dropdown.
Next, we have to enter the following:
- Name - “Articles”
- Slug (Automatically generated) - “articles.”
- Project folder - Root
- Content-Type:
- Here, we’ll select Choose existing {1} and select Pages {2}
Once that's done, we can create the folder by clicking on Create {3}.
Now, we can proceed to create a new story for our article home page with the following configuration:
- Name - “home”
- Slug - “home (auto-generated)
- Parent folder - Articles
- Define as root for the folder -
- Content-Type - Page
Once that’s done, click on Create.
Next, add a Hero block to the page, enter some content, and save it, as shown below.
Next, we’ll create a new folder for our articles.
Now let's proceed to create another story for our example article with the following configuration:
- Name - my first article {1}
- Slug - my-first-article {2} (auto-generated)
- Parent folder - Articles {3}
- Content-Type - Page {4}
Click on Create {4} to create a new story.
Create Article Hero block
Before we proceed, let’s go to our block library and create a new block for our article story. Click on the + New Block button, give the new block the technical name of “Article Hero,” and select “Nestable Block” as its Block type.
This block is going to have the following schema:
- "title" - text (This will be the article title)
- "description"- Text (This will have the article's subtitle)
- "cover-image" -Asset (This will be the cover image for the article)
Click on the Save button to save the changes. Now, in our story, we can add the Article Hero block and enter our content; here’s mine below.
Create Content block
Now we’ll create a new Content block that will hold the markdown content for our article. Create a new block from the block library with the following configuration:
- Technical name - “content”
- Block type - Nestable block
Click on Add block to create the block and name it “Content.”
Now, we can edit our newly created block and add the schema:
- “body” - Richtext
- This will be the body of the article
Once we've saved the changes for our content block, we can proceed to add our content to the page.
Now we can create our components in Remix, render them using the Storyblok React SDK and make changes from the Visual editor.
Rendering Components with the Storyblok React SDK
The Storyblok SDK allows us to create layouts by rendering components based on the data we get from our Storyblok space. We will create React components that link to the component structures in our Storyblok space.
First, let's create a regular site header component for navigation.
Create SIteHeader component
First, we’ll create a simple site header component with a nav to navigate through our app. Create a new file ./app/components/SiteHeader.tsx:
Next, we’ll build dynamic Storyblok components for the blocks in our Home page story in Storyblok.
Create Page component
This component is responsible for rendering HTML content for a Page content type; the Page content type has a property called the body, which includes a list of blocks.
Create a new file ./app/components/Page.tsx
:
Here, we’re using a few elements included in the @storyblok/react
package:
storyblokEditable
- This function marks a React component as "editable
". So, when the component appears inside the Visual Editor, we'll be able to click on it and edit its properties.StoryblokComponent
- This is a React generic component that allows us to render any React component linked to a Storyblok item. This component corresponds to the component type included in the storybook configuration list which we’ll soon create.
Let’s create the components for our blocks in Story blok including:
- A Hero component for the Hero block in our Home page story.
- An Article Hero component for the Article Hero block in our article story and
- A Content component for the Content block in our article story
Create Hero component
Create a new file ./app/components/Hero.tsx
:
Create Article Hero component
Create a new file, ./app/components/ArticleHero.tsx
and enter the following:
Create Content component
Create a new file, ./app/components/Content.tsx
:
Here, you can see that to render the rich text content of the body field of our content blok. We use the renderRichText
function available from the Storyblok SDK. We pass block?.body
, which contains the rich text data to renderRichText()
, this generates HTML.
To render the HTML, we pass it to dangerouslySetInnerHTML
on the body element.
Set up Tailwind Typography
In order for our rendered rich text to display properly, we’ll use the Tailwind Typography plugin. To install, run:
Then add the plugin to our tailwind.config.js file:
Awesome. Now with the .prose
class we added earlier, our rich text content will be displayed properly.
Configure the list of components
As stated earlier, 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 app/root.jsx
and add a new components parameter to the storyblokInit
call:
Next, in our ./app/routes/index.tsx
route, we have to update our query to fetch the Storyblok space and pass the data to the StoryblokComponent
component.
In ./app/routes/index.ts
enter the following:
Now, if we go to our Storyblok dashboard, we should see our home page and be able to edit it. We can also save the edits by clicking on the Save button.
Next, we can create a /articles
route in our application which will display a list of our articles.
Create ArticlesList component
Let’s create a standard component that displays a list of articles, create a new file ./app/components/ArticlesList.tsx
To get a list of articles, however, we’re going to make a query that does just that in our articles/route
With that said, we should get something like this:
Create a dynamic route for articles
To create a dynamic route that displays the content of an article from the slug, we have to do a few things in our dynamic route:
- Create a loader that gets the slug from the params
- Pass the slug as a variable to the query to fetch the page item
Let’s create a new file - ./app/routes/articles/$slug.tsx
and enter the following:
Now, our first article page should show us our article content:
Awesome! So far, we’ve covered almost all the basic and most important parts of creating a Remix application with Storyblok GraphQL.
In the following sections, we’ll look at a few other features of Storyblok.
Optimizing images with the Image service
This section will briefly show how we can use the Storyblok image service to deliver optimized images.
The Storyblok image service is handy as it provides high-quality images in different formats and sizes for all devices and platforms.
It covers many use cases, and the best way to learn about it and start using it in any application is through the official guide.
For now, we will use it to optimize our images. For that, we can append /m/to the end of our URL in our ArticlesHero component.
For the ArticlesHero component, in ./app/components/ArticleHero.tsx
:
Currently, our article images will be served in an optimized WebP format in supported browsers. You can read more on Storyblok image service here.
Conclusion
So far, we’ve covered how to build a simple GraphQL-enabled Remix application with Apollo Client. We also covered connecting our application to the Storyblok GraphQL API to get data. We used the Storyblok React SDK to render our components and make them editable in the Visual editor.
We also explored other features of Storyblok, particularly the image service, which allowed us to optimize our images.
It doesn't end there, though. We can further improve our application by exploring Storyblok’s features and services.
Further reading & Resources
Here are a few useful reads and links to the example code:
Resources | Links |
---|---|
GraphQL Content Delivery API | https://www.storyblok.com/docs/graphql-api |
Add a headless CMS to Remix in 5 minutes. | https://www.storyblok.com/tp/headless-cms-remix |
How to use Storyblok's GraphQL endpoint with React and Apollo | https://www.storyblok.com/tp/storyblok-graphql-react-apollo |
How to use Apollo Client with Remix | https://www.apollographql.com/blog/apollo-client/how-to-use-apollo-client-with-remix/ |