Deploying a full blown React website with Stormkit and Storyblok
Tags: serverless, jamstack, stormkit, react
Published: 2019-11-07 06:13:10

I’ve been using [my website](https://vsk.me) as a place to share my CV so far. It's a [create-react-app](https://github.com/facebook/create-react-app) bootstrap hosted on [Stormkit](https://www.stormkit.io). Lately, I’ve decided to add a blog section to it. A friend of mine was using [Storyblok](https://www.storyblok.com/) 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](https://github.com/facebook/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](https://www.storyblok.com/) 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](https://github.com) or [Bitbucket](https://bitbucket.org) account and push the repository to one of these providers. We’ll use this to connect our repository to [Stormkit](https://www.stormkit.io) and deploy our website.

## Prepare the content

First head to [Storyblok](https://www.storyblok.com/), create an account if you haven't and create a workspace.

![Img](//a.storyblok.com/f/68531/1024x431/f968b318c8/screenshot-2019-10-27-at-12-34-00.png)

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](//a.storyblok.com/f/68531/1024x190/343678d4a2/screenshot-2019-10-27-at-12-32-44.png)

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](//a.storyblok.com/f/68531/1024x190/243ec3e321/screenshot-2019-10-27-at-12-36-05.png)

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.

```javascript
// 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.

```javascript
// 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:

```javascript
// 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](https://www.stormkit.io). 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](https://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](//a.storyblok.com/f/68531/1024x535/e3d9af0ad6/screenshot-2019-10-27-at-23-02-16.png)

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](https://www.stormkit.io/docs/deployments) for relevant documentation for configuring your environment.

![Img](//a.storyblok.com/f/68531/1024x542/447db15efe/screenshot-2019-10-27-at-23-21-31.png)

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](https://twitter.com/savasvedova) if you have any remarks, questions, or simply want to connect.

## Disclaimer

I'm the founder of [Stormkit](https://www.stormkit.io) and I am strongly biased towards the tool. However, this doesn't mean what I say about the tool is exaggerated 😜

**Original Post:** https://vsk.me/blog/deploying-a-full-blown-react-website-with-stormkit-and-storyblok