Skip to main content

Create Custom Components in Storyblok and Gatsby.js

Contents
    Try Storyblok

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

    Hint:

    Please note that this article has already been updated to match Storyblok V2. If you haven’t already started using it, you can find out how to make the switch here.

    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 are in a hurry, check out this demo in boilerplate repo!

    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.

    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.

    app.storyblok.com
    Block Library
    1

    Block Library

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

    app.storyblok.com
    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

    app.storyblok.com
    Features with Images

    Features with Images

    Creating a component

    Now, let’s create a new Hero component. To do that, we will need to return to the Block Library {1} and click on +New Block at the top right corner {2}.

    app.storyblok.com
    Block Library
    1
    2

    Block Library

    Once we click on +New Block, we can enter the details about our new component. Let’s give it a name hero-section {1}, and select the block type Nested {2}, which is there by default. This will be a Nested component because we will be using it inside other components.

    Hint:

    A Content Type Block allows you to create templates for Stories, whereas a Nested Block can only be used inside a Story and other blocks. You can find more information here.

    You can hit Add Block {3} after that.

    app.storyblok.com
    Add Block
    1
    2
    3

    Add Block

    We can start adding the fields. We need to create four fields, three of which are-

    1. title of type text.

    2. subtitle of type text.

    3. image of type asset and then select image as we did for feature’s image.

    The 4th one will be a layout field, which will allow us to change the layout of the hero-section. We will have two different layouts - one with the image on the left-hand side and the other one with the image on the right-hand side. It should be of type Single Option {1}.

    app.storyblok.com
    Layout Field
    1

    Layout Field

    Now, let’s add two options as key-value pairs {1} for the two types of layout, each one with a default value {2}.

    app.storyblok.com
    Options for Layout
    1
    2

    Options for Layout

    Let’s hit save and add this component to our home story. Also, we should add content to the component’s fields. Feel free to use any text and image.

    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 -

    app.storyblok.com
    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.

    app.storyblok.com
    Hero Section with layout two

    Hero Section with layout two

    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. (Soon to be published. Stay tuned!)

    Developer Newsletter

    Want to stay on top of the latest news and updates in Storyblok?
    Subscribe to Code & Bloks - our headless newsletter.

    An error occurred. Please get in touch with marketing@storyblok.com

    Please select at least one option.

    Please enter a valid email address.

    This email address is already registered.

    Please Check Your Email!

    Almost there! To confirm your subscription, please click on the link in the email we’ve just sent you. If you didn’t receive the email check your ’junk folder’ or