Almost EVERYONE who tried headless systems said they saw benefits. Download the state of CMS now!

Storyblok now on AWS Marketplace: Read more

O’Reilly Report: Decoupled Applications and Composable Web Architectures - Download Now

Empower your teams & get a 582% ROI: See Storyblok's CMS in action

Skip to main content

Create Custom Components in Storyblok and Gatsby.js

Try Storyblok

Storyblok is the first headless CMS that works for developers & marketers alike.

  • Home
  • Tutorials
  • Create Custom Components in Storyblok and Gatsby.js
Hint:

If you'd like to build a multilanguage website with Gatsby, please follow this tutorial instead. If you'd like to build a website without internationalization logic, please keep following the rest of this article and the Ultimate Tutorial articles.

In this short tutorial, we will see how to start making our own components and extend the existing ones with Gatsby.js and Storyblok. We will add a new Hero component with two Layout options in Storyblok, along with the code in our frontend. We will also extend our existing Feature component by including images and better styling.

Hint:

If you’re in a hurry, have a look at our Ultimate Tutorial GitHub repo part 4 branch!

Section titled Requirements Requirements

This is a part of the Ultimate Tutorial Guide for Gatsby.js. You can find the previous part of the series here, which shows how to render Storyblok stories dynamically in Gatsby.js. We recommend you to take a look at that tutorial before starting this one.

Hint:

We will use the code from the previous tutorial as a starting point. You can find the boilerplate repo for it here.

Section titled Changing the Feature Component Changing the Feature Component

To view all the components, click on the Block Library tab {1} on the left-hand side as seen in the image.

Block Library
1

Block Library

Now, let’s edit the Feature’s schema, and add image as a new field {1}.

Add Image Field
1

Add Image Field

We will have to change the code in our Gatsby.js app with the updated fields and styles.

Replace the code of feature.js to the following:

components/feature.js
        
      ...

const Feature = ({ blok }) => (
  <div className="column feature" {...storyblokEditable(blok)}>
      <div className="p-6">
          <img className="object-cover object-center w-full mb-8 lg:h-48 md:h-36 rounded-xl" src={blok.image.filename} alt="feature"/>
          <h1 className="mx-auto mb-8 text-2xl font-semibold leading-none tracking-tighter text-neutral-600 lg:text-3xl">{blok.name}</h1>
          <div className="mt-4">
              <a href="#" className="inline-flex items-center mt-4 font-semibold text-blue-600 lg:mb-0 hover:text-neutral-600" title="read more"> Read More » </a>
          </div>
      </div>
  </div>
);

...
    

Let’s go ahead and add images to the features of a grid on the about page. To do that, we need to click on the Grid and then click on one of the features inside it. We will see that we have a field named image already present there. We can now add an image to a feature. Let’s add images for other features as well. It should look something like this -

HINT:

For Gatsby Image, we have a tutorial you can follow here

Features with Images

Features with Images

Section titled Creating a Hero Component Creating a Hero Component

Before creating the schema for this new block, let’s first of all consider what we would like it to look like and what options we want to provide. I would say that a headline, a subheadline and a background_image field would be a great place to start. However, let’s kick it up a notch and provide the option to make this hero component use the full width of the screen.

First, go to the Block Library {1} and create a New Block {2}.

Creating a new block in the Block Library
1
2

Creating a new block in the Block Library

It should be a Nested block {1} with the name hero {2}.

Creating a new nested block
1
2

Creating a new nested block

Now we can create our first three fields:

  • headline: field type Text
  • subheadline: field type Text
  • background_image: field type Image

The required steps for this are exactly the same as we have taken to add an image field to the Feature component earlier in this tutorial.

Once these fields are ready, we can create the layout field to make it possible to choose between two different layouts. Let’s add the field and choose Single-Option as its type {1}.

Creating a single-option field
1

Creating a single-option field

Let's add two key-value pairs which represent the possible choices {1}, hide the empty option {2}, and set the default value to constrained {3}:

Defining the layout options for the hero component
1
2
3

Defining the layout options for the hero component

Finally, save the component and add it to our Home story, right above the Teaser. You can already add some sample content to the fields. Of course, nothing will be shown in our frontend just yet. So let’s take care of that next, shall we?

Section titled Rendering the Hero Component in Gatsby Rendering the Hero Component in Gatsby

As we added a new component, we will also need to add the component to the frontend code of our project. Create a hero-section.js file inside the components folder, and add the following code to it.

components/hero-section.js
        
      import { storyblokEditable } from "gatsby-source-storyblok";

const HeroSection = ({ blok }) => {
    return (
        <div {...storyblokEditable(blok)}>
            {blok.layout == 'layout_one' && (
                <section className="text-gray-600 body-font">
                <div className="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
                  <div className="lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center">
                    <h1 className="title-font sm:text-5xl text-3xl mb-4 font-medium text-gray-900">{blok.title}
                    </h1>
                    <p className="mb-8 leading-relaxed">{blok.subtitle}</p>
                    <div className="flex justify-center">
                      <button className="inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg">Know More</button>
                      <button className="ml-4 inline-flex text-gray-700 bg-gray-100 border-0 py-2 px-6 focus:outline-none hover:bg-gray-200 rounded text-lg">Contact Us</button>
                    </div>
                  </div>
                  <div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6">
                    <img className="object-cover object-center rounded" alt="hero" src={blok.image.filename}/>
                  </div>
                </div>
              </section>
            )}
            {blok.layout == 'layout_two' && (
                <section className="text-gray-600 body-font">
                <div className="container mx-auto flex px-5 py-24 md:flex-row flex-col items-center">
                  <div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6 mb-10 md:mb-0">
                    <img className="object-cover object-center rounded" alt="hero" src={blok.image.filename}/>
                  </div>
                  <div className="lg:flex-grow md:w-1/2 lg:pl-24 md:pl-16 flex flex-col md:items-start md:text-left items-center text-center">
                    <h1 className="title-font sm:text-5xl text-3xl mb-4 font-medium text-gray-900">{blok.title}
                    </h1>
                    <p className="mb-8 leading-relaxed">{blok.subtitle}</p>
                    <div className="flex justify-center">
                      <button className="inline-flex text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg">Know More</button>
                      <button className="ml-4 inline-flex text-gray-700 bg-gray-100 border-0 py-2 px-6 focus:outline-none hover:bg-gray-200 rounded text-lg">Contact Us</button>
                    </div>
                  </div>
                </div>
              </section>
            )}
        </div>
    )
}

export default HeroSection
    

Note that we are changing the styles of the Hero section depending on the selected layout. This is being done conditionally on lines 6 and 24.

The only thing left to do is adding this component to our list of dynamic components in layout.js.

layout.js
        
      ...

import Teaser from './teaser'
import Grid from "./grid"
import Feature from "./feature"
import Navigation from "./navigation"
import Footer from "./footer"
import HeroSection from './hero-section';

storyblokInit({
  accessToken: process.env.GATSBY_PREVIEW_STORYBLOK,
  use: [apiPlugin],
  components: {
    teaser: Teaser,
    grid: Grid,
    feature: Feature,
   'hero-section': HeroSection
  }
});

...
    
Hint:

Look at how we use the technical name hero-section here to map with our frontend component.

Save and go back to our Home story in Storyblok. We will see something like this -



Hero Section with layout one

Hero Section with layout one

We also have an option to change the layout here. We can choose the other one from the dropdown and we will see the changes.

Hero Section with layout two

Hero Section with layout two

Section titled Wrapping Up Wrapping Up

In this tutorial, we saw how to extend and create new components from scratch with different types of fields, along with the integration of those components into the frontend of our application. Congratulations!

Next Part:

In the next part of this series, we will see how to create and render blog articles in Storyblok and Gatsby.js.

Author

Patrick Odey

Patrick Odey

Patrick is a Software Engineer with a passion for web technologies, Cloud computing and community building. He is from Nigeria and works as a Developer Relations Engineer at Storyblok. He is also a skateboarder.