HomeBlogCurriculumAbout meContact

Deploying a full blown React website with Stormkit and Storyblok

07. November 2019, 06:09

I’ve been using this website as a place to share my CV so far. It's a create-react-app bootstrap hosted on Stormkit. Lately, I’ve decided to add a blog section to it. A friend of mine was using Storyblok and talking good about it so I wanted to give it a shot. In this tutorial I’ll share what it took for me to implement the blog section.

Workflow

After updating the code, I simply push to Github. Stormkit receives an event and builds the website. Once the deployment is successful, it automatically publishes it. So all I have to do is to merge to master. To update the content, I use StoryBlok. Pretty cool and intuitive CMS. Using a combination of StoryBlok and Stormkit, I can preview the changes of the blog post directly on the website and then publish the content.

Let's get started

I'll assume you already have an application bootstrapped. I've used create-react-app for this, but you can use whatever you feel comfortable with.

First of all, let's install the requirements:

yarn add storyblok-js-client react-markdown

These packages will be used to make an API call to Storyblok to fetch the content, and to transform the returned markdown into a react component. More on this will come later.

Second, make sure to have either a Github or Bitbucket account and push the repository to one of these providers. We’ll use this to connect our repository to Stormkit and deploy our website.

Prepare the content

First head to Storyblok, create an account if you haven't and create a workspace.

Img

Create a new space and choose Headless as the type. Next, we’ll be adding a new folder. You can group your content using folders. Click on the + Folder button and name your folder. In my case I picked posts as a name. Leave the other settings empty. Afterwards, click on + Entry button and create a new entry. Pick a title and a slug for the entry. You’ll be using the slug to read it from the URL and fetch the content, therefore pick something that is SEO compatible.

Img

After you have created the folder and your first entry, you can define the schema for your posts. Click on Define schema and specify the fields that you will need. By default, Storyblok creates a body field with a default field type. Change that to markdown. After, you can add all the fields you will need. In my case, I have added a keywords, title, short content and description field**.** The ‘keywords’ and ‘description’ fields are used for SEO meta tags and the ‘short content’ is used for displaying a short description to the user when listing the latest entries.

Img

To add new entries, you will be using the + Entry button again on the entries page. Make sure that the posts is selected when adding new entries.

The code

Storyblok comes with a JS library that helps us making requests and retrieve content. At the beginning of this blog post, we have installed its official package. We'll using that one to make requests. Since the same client will be used across the application, I decided to create a global context and pass the client around with this context. I’ve created a file that exports the provider and a helper function to inject the context values.

// App.context.js

import React, { createContext } from "react";

const context = createContext();

export default {
  Provider: context.Provider,
  
  withConsumer: Component => props => (
    <context.Consumer>
      {(value = {}) => <Component {...props} {...value} />}
    </context.Consumer>
  )
};

Now let's create the provider and pass down the client object that we’re going to create at application load. The code is commented for further information.

// App.js

import React from "react";
import context from "./App.context";
import StoryblokClient from "storyblok-js-client";


// Create the Storyblok client. You can obtain the API-Key from 
// the Storyblok dashboard, by clicking on Settings and then api keys tab.
const Storyblok = new StoryblokClient({
  accessToken: "<your_api_key>",
  cache: {
    clear: "auto",
    type: "memory"
  }
});

const App = () => (
  // Wrap the application with a global context, and pass down the Storyblok client.
  // If we decide to add server-side rendering, we can create a new instance per request 
  // and pass it down the client without a race condition.
  <context.Provider value={{ storyblok: Storyblok }}>
    {/* your app goes here */}
  </context.Provider>
);

export default App;

Finally, we need to fetch the content. To do so, we need to connect the component that will display the latest blog entries to the context, retrieve the client and use the Storyblok API to fetch the items. Here is how:

// Posts.js

import React, { useEffect, useState } from "react";
import context from "./App.context";
import Markdown from "reach-markdown";

let unmounted = false;

const Posts = ({ storyblok }) => {
  const [entries, setEntries] = useState();
  
  useEffect(() => {
    // After the first call entries will be an array. 
    // Do not re-fetch after the first call.
    if (typeof entries !== "undefined") {
      return;
    }

    Storyblok.get("cdn/stories", {
      starts_with: "posts/",
    }).then(response => {
      // If there is a race condition and the component is unmounted before
      // the api call is completed, do not update the state as React will 
      // complain about memory leak.
      if (unmounted === false) {
        setEntries(response.data.stories || []);
      }
    });

    return () => {
      unmounted = true;
    }
  }, [storyblok, setEntries, entries])

  if (typeof entries === "undefined") {
    return "Nothing found.";
  }

  return (
    <div className="posts">
      <h2>Latest entries</h2>
      {entries.map(entry => (
        <div className="post">
          <div>{entry.content.title}</div>
          <div>
            <Markdown source={entry.content.body} />
          </div>
        </div>      
      ))}
    </div>
  )
}

export default context.withConsumer(Posts);

Deploying the application (manually)

Disclaimer: Once you have configured the environment (manually or through stormkit.config.yml), you can deploy automatically on each commit. In this tutorial, I'll show how to deploy manually.

In order to deploy our application, we’re going to use Stormkit. It makes deploying and managing web applications extremely easy. With Stormkit you can create isomorphic applications that have multiple environments, publish new versions to a percentage of users, implement remote configuration and conduct experiments with a/b testing. Autoscaling and TLS certificates are included and you don't have to worry about it.

Let's move on an connect our application:

  1. Go to app.stormkit.io and select your provider
  2. Once logged in, Stormkit will ask you in which provider your codebase is located. Click on the provider once more.
  3. If Github, click on ‘Connect more repositories’ and grant access to Stormkit.
  4. Next select your repository and now it's connected.

Once you have followed the steps above, you’ll be presented a page where you see the production environment:

Img

Click on Details under the production environment. You’ll be brought to a page where you can deploy your application. On top right of the screen you’ll see a Deploy now button. Click on that. If you’ve bootstrapped your application using create-react-app the default settings should be enough. Otherwise check here for relevant documentation for configuring your environment.

Img

Once you have deployed your application, Stormkit will generate a URL for you. Preview your application using that link. Later, you can connect your domain and publish this deployment so that the users will start to see that version of your application. You can also do staged rollouts by publishing multiple versions at the same time.

Conclusion

If you were able to follow until here, you should now have a serverless application that is hosted on Stormkit, and the backend is provided by Storyblok. All of this without touching a single line of server configuration. Pretty neat, huh?

To sum up; in essence we:

  • Created a workspace with Storyblok
  • Connected our repo to Stormkit for deployments
  • Installed requirements
  • Created a global context to pass down the Storyblok client and
  • Fetched the latest entries using the Storyblok client

Hope you enjoyed the tutorial. Follow me on twitter if you have any remarks, questions, or simply want to connect.

Disclaimer

I'm the founder of Stormkit and I am strongly biased towards the tool. However, this doesn't mean what I say about the tool is exaggerated 😜