Headless CMS for Metalsmith
Storyblok is the first headless CMS that works for developers & marketers alike.
The idea of Metalsmith is quite simple: "An extremely simple, pluggable static site generator". So in this quick walkthrough, we will have a look at how we can use the data from the Storyblok API with a Metalsmith project to create some pages. At the end of this article, you will have a Metalsmith project which renders components filled with data from the Storyblok API.
We're about to use Storyblok as headless CMS - so what is it?
Let me start with a short explanation of Storyblok: It is a hosted headless CMS in which you can create nested components per content entry. The author of one content entry, therefore, can create components that act as Content-type like articles or products but also easily can create nestable components to create landing pages - but would allow you to add Storyblok to existing solutions to enrich your current content as well.
What are we going to build?
During this article, we will use the default component page
which acts as layout/content-type and the nested components teaser, grid and feature to create a sample layout for landing pages. You will receive this setup during this tutorial, however, if you've already created a space that structure is already available in the content entry with the name "home".
Let’s start with Metalsmith
Requirements
- Basic understanding of Metalsmith itself
- NodeJS
- NPM
- Basic Handlebars knowledge
Installation
You can either use the JavaScript API or their CLI - we will go for the JavaScript API, actually we will use one of their examples as starting point.
You can either clone the whole repository to access their example called static-site
or use this link to download only that subfolder. I would recommend you to download only the subfolder to be honest.
Navigate to your static-site
folder in your cloned(downloaded project and execute:
Finally - let's build your downloaded Metalsmith project, you could either use their Makefile (make build
) or simply go with:
By default you should now have a simple "blog setup" using Markdown files and end up as static files in your build
folder. At this point you won't see a server running on your side - we will add express
to serve those static files later. You can do this step node index.js
after each step below to have a look what changes or what will be added.
If you're facing any troubles feel free to leave a comment below or check out their Github Repository.
Install the Storyblok JavaScript SDK.
By creating a new Storyblok space you should already have a basic setup of content components including teaser, grid, and feature which we're going to use in this tutorial.
To access data from Storyblok we will have to call the API to receive that data. The easiest way to do that in Metalsmith is to write a custom plugin and write some lines of JavaScript. We will use the Storybloks JavaScript SDK to load that data:
You should now have that dependency in your package.json
- since we want to use it we have to require it in the index.js.
Add the following line in your index.js
:
Let's create a Storyblok space
If you're not registered already simply sign up using the web-interface. You will be guided through the creating of your first space.
The default set of components and a content entry called "home" will be generated for you. You will also see the on-boarding with some code examples in different programming languages by clicking on that "home" content entry.
You will also see the preview token (sometimes called private token) which allows us to load draft versions of your content, you can copy that from one of the code examples or from your space dashboard. We will need this token in the next step, because we're about to load that!
Create a simple plugin to inject Storyblok content entries in the Metalsmith "files".
To allow a generation of pages according to your content in Storyblok, we will write a custom plugin to load the data from the API. You can customize it as you want and transform the data as you need it. It uses the Storyblok Content Delivery API and the endpoint Stories in this example. Add the plugin below in your index.js
you can read the comments to see what's happening - you can also console.log
the response.body
to have a look at the JSON from the API.
Add the content below to your index.js
:
Use your custom plugin
Since everything in Metalsmith is done using plugins, all we need to do is to tell Metalsmith to also use our storyblok
plugin. You can do this by simply adding .use(storyblok)
to their plugin chain.
Simply replace your current Metalsmith task from above with the code below - or enter the new line right above the .source('(./src')
in your index.js
:
Our first custom layout
Since the default Metalsmith setup ships with layouts/post.html
and layouts/layout.html
we simply reuse that structure by adding our own layout. The root component (eg. content-type) by default in Storyblok is called "page". By accessing the API of Storyblok you can already see that page
component.
You can create as many content-types as you want, to allow different layouts and fieldsets (think of something like: post
, project
or similar).
Let's create the new file layouts/page.html
for now:
By executing
again you can see it's actually nothing more than simple HTML page (build/home/index.html
) using the rendering engine Handlebars to output the loaded data from the Storyblok API. We're not using the field contents
here since most plugins out there expect it to be a Buffer and would break if it is not (or if it was empty). However, you can change the behavior of the plugin as you like.
The custom plugin loads defines the layout
according to the first component (content-type) in the content. As mentioned by default Storyblok comes with a content-type named "page". You can create new components such as this as you want. By creating a file layouts/page.html
with the above content, you can see how the data you receive from Storyblok actually looks like.
The page component contains a field called body
which is a simple array of other (now with the flag nestable) components. Let's try to iterate through that array called body
and output the containing components. I've removed the <html>
, <head>
, and <body>
tag from the page.html
contents beflow to reduce the length a little bit.
Exchange the <div class="root">
of the layouts/page.html
with the one below:
Don't forget to execute node index.js
to see the changes you just made.
Use Handlebars partials to create reusable components
Handlebars ships with the possibility to define "partials", which are reusable code snippets. In Storyblok we call those reusable parts simply "Components", actually everything in Storyblok is a component - some are acting as content-types other are nestable - those are the nestable once. The content we received from the API already has the components (teaser
, grid
and feature
) of which we saw two already above. Now let's switch to the Handlebars partials to allow dynamically add those new components.
In your index.js
you can replace we will have to tell the metalsmith-layouts
plugin that we're not only using handlebars
but also their partials options, which is bascially adding a line to tell the plugin in which folder to look for our partials.
Add the content below to your index.js
:
and of course we will have to iterate through those components and include them. Handlebars allows us to dynamically include partials by default.
New content for the layouts/page.html
:
We're about to include teaser
and grid
as those are the components we could see before, to allow Handlebars to include them, we need to create the files in the folder partials
.
Create partials/teaser.html and partials/grid.html
Above we can see that all information for one such component is in the blok
property and therefore we are able to access all information of a component like shown below.
Content for partials/teaser.html
and partials/grid.html
:
Use the fields of the teaser component
We can now see that every field defined in Storyblok for one component can be used as a property in Handlebars partial files. Let's access the headline
field of the teaser
component and output it as an actual headline not just as a JS object above, simply change the content of the partials/teaser.html
to.
Content for partials/teaser.html
:
The Grid: Let's move on to nested components
The grid
Storyblok already comes with, is a component with an array field called body
, similar to the page
component. Currently, its only purpose is to allow a nesting, so let's iterate through that field. As in the teaser
before we will access the body
simply using the body
property passed as context by Handlebars during the include in layouts/page.html
.
Content for partials/grid.html
:
You should now be able to see the name of the next nested components feature
. We also need a file for that particular component in the partials
folder as well, otherwise we won't be able to include it.
Nested component "feature"
You can create as many components with as many fields as you like - so you're not limited to those we've just created for you to get started faster. Create more components, nest them and arrange them to beautiful landing pages or enrich existing pages or posts, or create new component as content-type for things like posts, projects and similar.
Let's get back to the feature
component for now: Same as with the teaser
and grid
we can simply create a new file in partials
called feature
.
Content for partials/feature.html
:
and replace the content of the grid.html
component to actually include the nested feature
components:
Well done! Lets prepare editing!
Now you should have seen the key concept of Storyblok and our nested components, if you only need flat structures simply create a new component as content-type and add your required fields directly.
The next big thing is the way you and your content creators will be able to edit the content of such components. For this purpose, we will now add the Storyblok JavaScript Bridge you may have already seen in the Onboarding (Step 2) of Storyblok.
You can find a line similar to the one below in your onboarding screen add it to the bottom of your layouts/page.html
- right before the closing </body>
tag.
Now we need to update our components, in the draft version of the content from the Storyblok API each component ships with a _editable
property - containing an HTML comment, place this right before every partial we've just created - just like we did in the teaser
below:
Updated content for the partials/teaser.html
:
You can have a look at the HTML comment we've prepared there - it provides all the data the Storyblok JavaScript Bridge needs to tell the editor which component you've just clicked without messing up your own HTML, because it's only a comment.
Rebuild on content change
You can skip this - if you don't want to have an instant preview and prefer to run Metalsmith everytime in the console to see your changes - this won't be used in production (because you don't need that instant preview) and is not necessary to use the data added in Storyblok. You can also use our Webhooks to trigger a build pipeline.
Since Metalsmith generates static files we somehow need to trigger a rebuild after a save or publish event was fired in Storyblok to have an instant preview. To do so we're using the Events of the Storyblok JavaScript Bridge, simply add those lines below the script that you've included before.
Let's add a call to rebuild in the layouts/page.html
, right before the closing </body>
tag again:
You notice that it will call the /rebuild
route, which isn't available using a default Metalsmith setup because there is no server running. To allow us doing that we're about to add a simple Express server that serves the generated static files and allows us to trigger Metalsmith using a simple GET call. So let's install express using NPM.
We're now able to add some more JS to our index.js
. We'll have to require express
and add some lines of JavaScript to configurate our express server.
Add the content below to your index.js
:
You can see that we're calling a function build
which has another function as parameter. You can wrap your Metalsmith task in a function and instead of writing "Build finished" in the console we simply call that function - so let's update that. You can change the way we did that in this example anytime - feedback in Github or as comment down below appreciated.
Updated content for the Metalsmith task in your index.js
:
Now we can start our NodeJS app just like before, but now it will start an Express server on http://localhost:4000/:
You can extract that Express part into another .js
file like preview.js
and or define a task that you can start for that preview purpose - to keep that tutorial short I've just extended my current index.js
to showcase the basic idea.
You won't need this in production or wherever you want to publish your pages, this express server only needs to run to allow an instant preview since you don't want to run a command yourself everytime you've changed something in Storyblok. Of course, you could go with a command every time, or once after you've added your changes - but using it like this you will be much faster during content creation, because you see what you change!
Embed your local environment as preview source
The last step in the onboarding, and to finally allow you to edit your components in that visual editor, you can simply enter http://localhost:4000/ in the input at the onboarding screen. It will switch to your local address directly - you can change that later in your space settings of course.
I've added some simple styles to the components to make it look better than plain HTML. Simply include those styles to your project and you should see the screen below if you're working in Storyblok, you can change the appearance of all components as you want - since Storyblok only provides the content you're free to do whatever you want with that content.
Summary
Using Storyblok as your CMS with Metalsmith is, as you can see, just loading JSON instead of Markdown files. The visual editor can be plugged-in pretty straightforward and only needs a small amount of additional code for that instant rebuild. I really liked using Metalsmith because the idea of adding more functionality via plugins to allow that kind of flexibility is as flexible as Storyblok itself. I would love to receive and read your feedback and maybe have a look at some of your implementations. As always you can download the whole source code from Github and comment below.