DEV Community

loading...
Cover image for [Storyblok, Gatsby] Programmatically create blog post pages from data

[Storyblok, Gatsby] Programmatically create blog post pages from data

arisa_dev
Developer Relations Engineer at Storyblok. Love Aikido🥋 A free tech knowledge-sharing community, Lilac organizer💪 A host of Anonymous.fm https://dev.to/anonymousfm-arisa
Updated on ・6 min read

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, with free hands-on Frontend e-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

Storyblok: The Complete Guide to Build a Full-Blown Multilanguage Website with Gatsby.js

This is also relevant tutorial blog post from them👇

Storyblok: Add a headless CMS to Gatsby.js in 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

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

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

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

Fill out something to test output for later.

Alt Text

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.

$ yarn add storyblok-rich-text-react-renderer
Enter fullscreen mode Exit fullscreen mode

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

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

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

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"

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

  • gatsby-node.js
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)
            }
          })
        })
      )
    })
}
Enter fullscreen mode Exit fullscreen mode

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

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.

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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

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

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.

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

Done!

We see our rich text data is displaying!

Alt Text

(🗒 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!

Discussion (0)