[Storyblok, Gatsby] Programmatically create blog post pages from data
Tags: storyblok, gatsby, react, api
Published: 2021-05-20 13:56:47

Hi there!

I'm Arisa, a DevRel from this June living in Germany🇩🇪 (A big announcement is coming this June😏)

I have a free online programming learning community called [Lilac](https://note.com/frontendlifeinde/m/m9b8feda1d547), with [free hands-on Frontend e-books](https://zenn.dev/arisa_dev/books)👩‍💻

# Who is this article for?

- Anyone wants to have a super blazing fast blog app with headless CMS integrated in it😎
- Anyone wants to build headless CMS integrated blog with the easiest way
- Anyone wants to try out Storyblok with Gatsby.js
- Anyone trying [Storyblok's Gatsby multilanguage blog tutorial](https://www.storyblok.com/tp/gatsby-multilanguage-website-tutorial)

> [Storyblok: The Complete Guide to Build a Full-Blown Multilanguage Website with Gatsby.js](https://www.storyblok.com/tp/gatsby-multilanguage-website-tutorial)

This is also relevant tutorial blog post from them👇

> [Storyblok: Add a headless CMS to Gatsby.js in 5 minutes](https://www.storyblok.com/tp/add-a-headless-cms-to-gatsby-5-minutes)

# Step 1: Create a post content type in Storyblok

Create a folder from Storyblok.

This folder will be a parent of each blog post.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gda3vlce09lcg3oaivg0.png)

If you already have a component created for a blog post, you can set up like the screenshot above.

If not, you can choose "Add new" and choose "Post".

Check the component type we chose or created before we go further.

It's always good to see the component data architecture in order to fetch later.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7t92nx7vfgujqn9qe894.png)

The best part is that as long as we choose "Post" content type, Storyblok automatically generates the blueprint for blog posts😎

# Step 2: Create blog entry page in Storyblok

Let's move on to create a single blog post page.

Go to "Blog" content type folder, the one we just created.

Click "Entry" to create a blog post entry.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/224pcgth0um6v2ejsars.png)

You'll be redirected to a single blog post visual editor screen.

Fill out something to test output for later.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/20mw7iw9qzjbm9l8yx2m.png)

One more prep before we go figure out routing.

Install `storyblok-rich-text-react-renderer` to render the rich text content.

Storyblok uses this to render rich text content.

```shell
$ yarn add storyblok-rich-text-react-renderer
```

# Step 3: Programmatically create pages from data

Gatsby.js dynamically generates routes for you.

To do that, we can choose from 3 options.

1. Define routes in src/pages
2. Using the File System Route API
3. Using gatsby-node.js 👈 Recommended

We'll use the 3rd option because it dynamically generates pages as we create new blog posts in Storyblok main dashboard.

With this option, editors and writers don't have to ask us to create every single blog post, right?

Option 2 is also possible but still, it creates pages under the `pages` directory, same as option 1.

> [Gatsby.js: Using gatsby-node.js](https://www.gatsbyjs.com/docs/reference/routing/creating-routes/#using-gatsby-nodejs)

Everything I set up in here is based on Gatsby.js's documentation.

If you're lost, their tutorial bog post is the best place to be back on track👍

> [Gatsby.js: Programmatically create pages from data](https://www.gatsbyjs.com/docs/tutorial/part-seven/)

By using `createPage` action from Gatsby, we can create our blog post pages dynamically.

I recommend everyone to take a look at Gatsby's Routing documentation and Storyblok's `gatsby-source-storyblok` GitHub repo README.

> [Gatsby.js: Routing](https://www.gatsbyjs.com/docs/reference/routing/creating-routes/)

> [storyblok/gatsby-source-storyblok](https://github.com/storyblok/gatsby-source-storyblok)

Especially, we'll use "Filtering on content type fields" section from Storyblok's `gatsby-source-storyblok` GitHub repo README.

> [storyblok/gatsby-source-storyblok: "Filtering on content type fields"](https://github.com/storyblok/gatsby-source-storyblok#filtering-on-content-type-fields)

It's just an example, but it might help to see my case.

- `gatsby-node.js` 

```javascript
const path = require('path')

exports.createPages = ({ graphql, actions }) => {
  const { createPage } = actions

  return new Promise((resolve, reject) => {
    const blogPostTemplate = path.resolve('./src/templates/blog-entry.js')

      resolve(
        graphql(
          `{
            posts: allStoryblokEntry(filter: {field_component: {eq: "Post"}}) {// 👈 the name of your component name
              edges {
                node {
                  id
                  name
                  slug
                  field_component
                  full_slug
                  content
                }
              }
            }
          }`
        ).then(result => {
          if (result.errors) {
            console.log(result.errors)
            reject(result.errors)
          }

          const allPosts = result.data.posts.edges

          allPosts.forEach((entry) => {
            // 👇 the name of content type
            if(entry.slug !== "blog") {
              const page = {
                  path: `/${entry.node.full_slug}`,
                  component: blogPostTemplate,
                  context: {
                    story: entry.node
                  }
              }
              createPage(page)
            }
          })
        })
      )
    })
}
```

To focus on blog post page, I only included blog post page generation code.

Personally, instead of just copy and paste the above, take a look at whether you succeeded to fetch data or not in GraphiQL from Gatsby.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y3zujd6gfumrzet4thsl.png)

Just a quick note.

If you copy and paste the data path from `gatsby-node.js` source code from above, it only returns an empty array.

It's just that the architecture to write in JS CLI and GraphiQL is different.

Make sure to run `yarn develop` again.

# Step 4: Create a blog entry template

By default, Gatsby already provides us `pages/index.js` in their starter.

This file is a template for non-blog post pages, like Home, About etc...

For the blog post page, we'll use this trick with a bit of arrangement.

Create `templates/blog-entry.js` file.

If you're smart, you already recognized that we already imported this file in `gatsby-node.js` file.

That's the trick we'll be using 😎

Write your code something like this.

```javascript
import React from 'react'
import Layout from '../components/Layout'
import Seo from '../components/seo'
import useStoryblok from '../lib/storyblok'

export default function BlogEntry({ pageContext, location }) {
  let story = pageContext.story
  story = useStoryblok(story, location)

  return (
    <Layout>
      <Seo title={`Blog | ${ story.content.title }`} />
      <div>
        <div>
          <h1>{ story.content.title }</h1>
        </div>
        <img
          src={`https://${ story.content.image }`}
          alt={ story.content.image }
        />
      </div>
    </Layout>
  )
}
```

This time, we'll test out to see the blog post title and an image.

How did I know the path to fetch data?

It's all in a draft JSON from Storyblok ✨

Go to the header menu -> down arrow -> "draft JSON".

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yr9ffk8hls6emv7ik7o3.png)

It'll open a new browser page with a draft JSON with all the data path you need.

Done!✨

You'll see your very first blog post with a title and an image👏

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axvz7pmtou8ok29yrx71.png)

If you see a 404 page from Gatsby, take a closer look.

You'll find a new page we just created on the page lists.

Click that, and you'll see a result like above.

We managed to route, so the URL of our very first blog post is following from what we named!

As long as we were able to get data for this blog post title and image, we're good to go to fetch rich text data.

In this case, our blog main contents.

# Step 5: Fetch rich text data

In order to do so, we need 1 thing to prepare.

Remember we installed `storyblok-rich-text-renderer` ?

Now is the time to use this gem 💎

Import `storyblok-rich-text-renderer` in your `templates/blog-entry.js` file.

Also, use `render` to render a rich text data.

```javascript
import React from 'react'
import Layout from '../components/Layout'
import Seo from '../components/seo'
import useStoryblok from '../lib/storyblok'
// 👇 Import  `storyblok-rich-text-renderer` 
import { render } from 'storyblok-rich-text-react-renderer'

export default function BlogEntry({ pageContext, location }) {
  let story = pageContext.story
  story = useStoryblok(story, location)

  return (
    <Layout>
      <Seo title={`Blog | ${ story.content.title }`} />
      <div>
        <div>
          <p>{ story.content.category }</p>
          <h1>
            { story.content.title }
          </h1>
          <p>{ story.content.intro }</p>
        </div>
        <img
          src={`https://${ story.content.image }`}
          alt={ story.content.image }
        />
      </div>
      <div>
        {/* render a rich text data */}
        { render(story.content.long_text) }
      </div>
    </Layout>
  )
}
```

Done!

We see our rich text data is displaying!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vsztf6nprmtx864tpbii.png)

(🗒 I added category tag and intro. You can add contents as you wish like this.)

Hope this blog post helps with what you were looking for!