Nutritious Polygonal Meatball

    gatsby-source-storyblok
    TypeScript icon, indicating that this package has built-in type declarations

    5.0.0 • Public • Published
    Storyblok Logo

    gatsby-source-storyblok

    The Gatsby source plugin you need to interact with Storyblok API and enable the Real-time Visual Editing Experience.


    gatsby-source-storyblok npm Follow @Storyblok
    Follow @Storyblok

    🚀 Usage

    If you are first-time user of the Storyblok, read the Getting Started guide to get a project ready in less than 5 minutes.

    Installation

    Install gatsby-source-storyblok:

    npm install gatsby-source-storyblok
    // yarn add gatsby-source-storyblok

    Compatibility

    Version to install Support
    Latest gatsby-source-storyblok Modern browsers + Node 16+. isomorphic-fetch is implemented.
    Version 4 gatsby-source-storyblok@4 Node 14 and lower Node with no Fetch API support

    Initialization

    Register the plugin on your application and add the access token of your Storyblok space. You can also add the apiPlugin in case that you want to use the Storyblok API Client: For Spaces created under US region, you should pass the region like { apiOptions: { region: 'us' } }. If your space is under EU, no further configuration is required.

    You need to declare the plugin use and its options in gatsby-config.js

    module.exports = {
      plugins: [
        {
          resolve: 'gatsby-source-storyblok',
          options: {
            accessToken: 'YOUR_TOKEN',
            version: 'draft',
            localAssets: true, // Optional parameter to download the images to use with Gatsby Image Plugin
            languages: ['de', 'at'] // Optional parameter. Omission will retrieve all languages by default.
          }
        }
      ]
    }

    src/components/layout.js

    import configuration from '../../gatsby-config'
    
    const sbConfig = configuration.plugins.find((item) => item.resolve === 'gatsby-source-storyblok')
    
    storyblokInit({
      accessToken: sbConfig.options.accessToken,
      // bridge: false,
      apiOptions: {
        region: "us", // Pass this key/value if your space was created under US region
      },
      use: [apiPlugin],
      components: {
        teaser: Teaser,
        grid: Grid,
        feature: Feature
      }
    });

    Add all your components to the components object in the storyblokInit function.

    That's it! All the features are enabled for you: the Api Client for interacting with Storyblok CDN API, and Storyblok Bridge for real-time visual editing experience.

    You can enable/disable some of these features if you don't need them, so you save some KB. Please read the "Features and API" section

    Getting Started

    gatsby-source-storyblok does three actions when you initialize it:

    • Provides a useStoryblokState custom hook in your app, that parses story content JSON into the object.
    • Loads Storyblok Bridge for real-time visual updates.
    • Provides a storyblokEditable function to link editable components to the Storyblok Visual Editor.

    1. Fetching Content

    Query data from GraphQL:

    src/pages/index.js

    import { useStoryblokState } from "gatsby-source-storyblok"
    
    import Layout from "../components/layout"
    
    const IndexPage = ({ data }) => {
      let story = data.storyblokEntry
      story = useStoryblokState(story)
    
      return (
        <Layout>
          <div>
            <h1>{story.name}</h1>
          </div>
        </Layout>
      )
    }
    
    export default IndexPage
    
    export const query = graphql`
      query HomeQuery {
        storyblokEntry(full_slug: { eq: "home" }) {
          name
          full_slug
        }
      }
    `

    Note: if you don't use apiPlugin, you can use your prefered method or function to fetch your data.

    2. Listen to Storyblok Visual Editor events

    Use useStoryblokState to get the new story every time is triggered a change event from the Visual Editor. You need to pass the originalStory as a first param. bridgeOptions (second param) is an optional param if you want to set the options for bridge by yourself:

    import { StoryblokComponent, useStoryblokState } from "gatsby-source-storyblok"
    
    import Layout from "../components/layout"
    
    const IndexPage = ({ data }) => {
      let story = data.storyblokEntry
      story = useStoryblokState(story)
    
      const components = story.content.body.map(blok => (<StoryblokComponent blok={blok} key={blok._uid} />))
    
      return (
        <Layout>
          <div>
            <h1>{story.name}</h1>
            {components}
          </div>
        </Layout>
      )
    }
    
    export default IndexPage
    
    export const query = graphql`
      query HomeQuery {
        storyblokEntry(full_slug: { eq: "home" }) {
          content
          name
          full_slug
          internalId
        }
      }
    `

    You can pass Bridge options as a third parameter as well:

    useStoryblok(story.internalId, (newStory) => setStory(newStory), {
      resolveRelations: ["Article.author"],
    });

    3. Link your components to Storyblok Visual Editor

    For every component you've defined in your Storyblok space, call the storyblokEditable function with the blok content:

    import { storyblokEditable } from "gatsby-source-storyblok";
    
    const Feature = ({ blok }) => {
      return (
        <div {...storyblokEditable(blok)} key={blok._uid}>
          <div>{blok.name}</div>
          <p>{blok.description}</p>
        </div>
      );
    };
    
    export default Feature;

    Where blok is the actual blok data coming from Storblok's Content Delivery API.

    As an example, you can check in our Gatsby.js example demo how we use APIs provided from React SDK to combine with Gatsby.js projects.

    import { StoryblokComponent, storyblokEditable, useStoryblokState } from "gatsby-source-storyblok"
    
    import Layout from "../components/layout"
    
    const IndexPage = ({ data }) => {
      let story = data.storyblokEntry
      story = useStoryblokState(story)
    
      const components = story.content.body.map(blok => (<StoryblokComponent blok={blok} key={blok._uid} />))
    
      return (
        <Layout>
          <div {...storyblokEditable(story.content)}>
            {components}
          </div>
        </Layout>
      )
    }
    
    export default IndexPage
    
    export const query = graphql`
      query HomeQuery {
        storyblokEntry(full_slug: { eq: "home" }) {
          content
          name
          full_slug
          internalId
        }
      }
    `

    Features and API

    You can choose the features to use when you initialize the plugin. In that way, you can improve Web Performance by optimizing your page load and save some bytes.

    Storyblok API

    You can use an apiOptions object. This is passed down to the storyblok-js-client config object:

    storyblokInit({
      accessToken: "YOUR_ACCESS_TOKEN",
      apiOptions: {
        // storyblok-js-client config object
        cache: { type: "memory" },
      },
      use: [apiPlugin],
      components: {
        teaser: Teaser,
        grid: Grid,
        feature: Feature,
      },
    });

    If you prefer to use your own fetch method, just remove the apiPlugin and storyblok-js-client won't be added to your application.

    storyblokInit({});

    Storyblok Bridge

    If you don't use useStoryblokBridge, you still have access to the raw window.StoryblokBridge:

    const sbBridge = new window.StoryblokBridge(options);
    
    sbBridge.on(["input", "published", "change"], (event) => {
      // ...
    });

    Rendering Rich Text

    You can easily render rich text by using the renderRichText function that comes with gatsby-source-storyblok:

    import { renderRichText } from "gatsby-source-storyblok";
    const renderedRichText = renderRichText(blok.richtext);

    You can set a custom Schema and component resolver globally at init time by using the richText init option:

    import { RichTextSchema, storyblokInit } from "gatsby-source-storyblok";
    import cloneDeep from "clone-deep";
    const mySchema = cloneDeep(RichTextSchema); // you can make a copy of the default RichTextSchema
    // ... and edit the nodes and marks, or add your own.
    // Check the base RichTextSchema source here https://github.com/storyblok/storyblok-js-client/blob/master/source/schema.js
    storyblokInit({
      accessToken: "<your-token>",
      richText: {
        schema: mySchema,
        resolver: (component, blok) => {
          switch (component) {
            case "my-custom-component":
              return `<div class="my-component-class">${blok.text}</div>`;
            default:
              return "Resolver not defined";
          }
        },
      },
    });

    You can also set a custom Schema and component resolver only once by passing the options as the second parameter to renderRichText function:

    import { renderRichText } from "gatsby-source-storyblok";
    renderRichText(blok.richTextField, {
      schema: mySchema,
      resolver: (component, blok) => {
        switch (component) {
          case "my-custom-component":
            return `<div class="my-component-class">${blok.text}</div>`;
            break;
          default:
            return `Component ${component} not found`;
        }
      },
    });

    Gatsby feature references

    With Gatsby's image

    You need to set the localAssets option to true. Here is an example of the usage:

    import { graphql } from "gatsby"
    import { GatsbyImage, getImage } from "gatsby-plugin-image"
    
    function BlogPost({ data }) {
      const image = getImage(data.file)
      return (
        <section>
          <GatsbyImage image={image} />
        </section>
      )
    }
    
    export const pageQuery = graphql`
      query {
        file(name: {eq: "demo"}) {
          absolutePath
          url
          childImageSharp {
             gatsbyImageData(
              width: 200
              placeholder: BLURRED
              formats: [AUTO, WEBP, AVIF]
            )
          }
        }
      }
    `

    With Gatsby's createPages

    For more info regarding createPages see the Gatsby docs: docs/reference/config-files/gatsby-node/#createPages

    2a. You need to create a template file to get the data from GraphQL

    import { useStoryblokState } from "gatsby-source-storyblok"
    import Layout from "../components/layout"
    
    export default function StoryblokEntry ({ data }) {
      const story = data.storyblokEntry
      story = useStoryblokState(story)
    
      return (
        <Layout>
          <div>{story.name}</div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query($slug: String!) {
        storyblokEntry(full_slug: { eq: $full_slug }) {
          internalId
          name
          full_slug
        }
      }
    `

    3a. After this, you need to create the pages for your application. For this, edit your gatsby-node.js.

    const path = require('path')
    
    exports.createPages = async ({ graphql, actions }) => {
      const storyblokEntry = path.resolve('src/templates/storyblok-entry.js')
    
      // querying the storyblok data from GraphQL data layer
      const { data } = await graphql(
        `query {
          allStoryblokEntry {
            edges {
              node {
                internalId
                full_slug
              }
            }
          }
        }`
      )
    
      // creating pages using createPage function like described in the documentation
      // https://www.gatsbyjs.org/docs/programmatically-create-pages-from-data/#creating-pages
      data.allStoryblokEntry.edges.forEach(edge => {
        const full_slug = edge.node.full_slug
    
        actions.createPage({
          path: full_slug,
          component: storyblokEntry,
          context: {
            slug: full_slug
          },
        })
      })
    }

    With Gatsby's File System Routes API

    For more info regarding The File System Routes API see the Gatsby docs: docs/reference/routing/file-system-route-api/

    2b. Create a collection route inside src/pages

    |-- src
       |-- pages
          |-- {storyblokEntry.full_slug}.js
    

    3b. Gatsby will use ths page template for each storyblokEntry

    import { useStoryblokState } from "gatsby-source-storyblok"
    import Layout from "../components/layout"
    
    export default function StoryblokEntry ({ data }) {
      const story = data.storyblokEntry
      story = useStoryblokState(story)
    
      return (
        <Layout>
          <div>{story.name}</div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query ($full_slug: String!) {
        storyblokEntry(full_slug: { eq: $full_slug }) {
          internalId
          name
          full_slug
        }
      }
    `

    The options object in details

    gatsby-config.js

    {
      resolve: 'gatsby-source-storyblok',
      options: {
        accessToken: 'YOUR_TOKEN',
        version: 'draft',
        resolveRelations: [''],
        includeLinks: false
      }
    }
    • accessToken: Your Storyblok draft token
    • version: 'draft' or 'published'
    • timeout: Optionally provide a timeout for the api request
    • resolveLinks: This will automatically resolve internal links of the multilink field type. If the value is story the whole story object will be included. If the value is url only uuid, id, name, path, slug and url (url is a computed property which returns the "Real path" if defined to use it for navigation links) will be included.
    • resolveRelations: Resolve relationships to other Stories (in the first level of nesting) of a multi-option or single-option field-type. Provide the field key(s) as array to resolve specific fields. Example: ['article.related_articles', 'article.author'].
    • includeLinks: If 'true' you can query links by allStoryblokLinkEntry. The links query lets you create a dynamic navigation tree as it includes also content folders.
    • languages: An array of strings that will be used in languages request instead of languages in space settings. Use it to only load the languages that you want to.

    How to query all Content Entries

    To get all entries unfiltered you can do the following query:

    {
      allStoryblokEntry {
        edges {
          node {
            id
            name
            created_at
            published_at
            uuid
            slug
            full_slug
            content
            is_startpage
            parent_id
            group_id
            internalId
          }
        }
      }
    }
    How to query filtering of content inside a folder

    The following example shows a filter to get all items from a news folder:

    {
      allStoryblokEntry(filter: {full_slug: {regex: "/^news\//"}}) {
        edges {
          node {
            name
            full_slug
          }
        }
      }
    }
    How to query filtering of languages

    If you use field level translations you can filter for a specific language using following query:

    {
      allStoryblokEntry(filter: {lang: {eq: "de"}}) {
        edges {
          node {
            name
            full_slug
          }
        }
      }
    }
    How to query filtering on content type fields

    Every field of your content types is available via the prefix field_.

    This lets you for example to query for a specific component:

    {
      allStoryblokEntry(filter: {field_component: {eq: "page"}}) {
        edges {
          node {
            name
            full_slug
          }
        }
      }
    }

    How to query a single content entry

    {
      storyblokEntry(slug: { eq: "global-navi" }) {
        content
      }
    }

    Datasources

    allStoryblokDatasource {
      edges {
        node {
          id
          value
          name
          data_source
        }
      }
    }

    How to query Datasource entries

    This will return all datasources, with or not dimensions values:

    allStoryblokDatasourceEntry(filter: { data_source: { eq: "DATASOURCE_SLUG" } }) {
      edges {
        node {
          id
          name
          value
          data_source
          data_source_dimension
        }
      }
    }

    If you want to filter by a specific dimension, you should use:

    allStoryblokDatasourceEntry(filter: { data_source: { eq: "DATASOURCE_SLUG" }, data_source_dimension: { eq: "DATASOURCE_DIMENSION_VALUE" } }) {
      edges {
        node {
          id
          name
          value
          data_source
          data_source_dimension
        }
      }
    }

    How to query links

    Use the links api to create a dynamic navigation tree. To use this query you need to add includeLinks: true in the plugin options.

    allStoryblokLinkEntry {
      edges {
        node {
          id
          uuid
          slug
          parent_id
          name
          is_folder
          published
          is_startpage
          position
        }
      }
    }

    🔗 Related Links

    ℹ️ More Resources

    Support

    Contributing

    Please see our contributing guidelines and our code of conduct. This project use semantic-release for generate new versions by using commit messages and we use the Angular Convention to naming the commits. Check this question about it in semantic-release FAQ.

    Install

    npm i gatsby-source-storyblok

    DownloadsWeekly Downloads

    6,642

    Version

    5.0.0

    License

    none

    Unpacked Size

    73.1 kB

    Total Files

    9

    Last publish

    Collaborators

    • lisi_linhart
    • christianzoppisb
    • jpcfreire
    • delooks
    • dominikangerer
    • emanuelgsouza
    • samuells