Add a CMS to Ruby on Rails in 5 minutes
Storyblok is the first headless CMS that works for developers & marketers alike.
In this short article, we will show you how you can use the API-based CMS Storyblok in combination with the Framework “Ruby on Rails ”. At the end of this article, you will have a Ruby on Rails application which renders components filled with data from Storyblok.
You can also clone the code of this tutorial at github.com/storyblok/rails-boilerplate .
1. Create a new Ruby on Rails project
You can add Storyblok to existing projects or new ones. I will show how to add Storyblok to a new project. Execute the following commands so you get a new project ready to start with:
rails new my-project
cd ./my-project
2. Add Storyblok's Ruby SDK
Let’s install the headless CMS client and liquid as template language by adding following lines to our Gemfile
.
gem 'liquid', github: 'Shopify/liquid', branch: 'master'
gem 'storyblok'
After adding the gems execute bundle install
and start the rails app.
bundle install
bin/rails server -p 3000
Create a Storyblok space
After you have started your project you need to get the Storyblok preview token and tell Storyblok where your app is running. Create a new Storyblok space and click on the "Home" content item. Note down your preview token
and insert localhost:3000
as your host.
3. Generate the pages controller
Storyblok is a page centric CMS so we create a controller that handles all requests with a wildcard route.
bin/rails g controller pages
Add the index method
Add following code to pages_controller.rb
and exchange YOUR_TOKEN
with the preview token from Storyblok's settings page.
class PagesController < ApplicationController
def index
response.headers['X-FRAME-OPTIONS'] = 'ALLOWALL'
client = Storyblok::Client.new(
logger: logger,
cache_version: Time.now.to_i,
token: 'YOUR_TOKEN',
version: 'draft'
)
assigns = {
story: client.story(params[:path])['data']['story']
}
Liquid::Template.file_system = Liquid::LocalFileSystem.new('app/views/components')
template = Liquid::Template.parse(File.read('app/views/layouts/page.liquid'))
render html: template.render!(assigns.stringify_keys, {}).html_safe
end
end
Extend the routes file
Add a wildcard route to route all requests to the pages controller.
Rails.application.routes.draw do
match "*path", to: "pages#index", via: :all
end
4. Add the page template
Add the main page template page.liquid
in layouts
which includes the Storyblok Javascript bridge . Optionally you can add a conditional check for the parameter _storyblok in the url to include the Storyblok's Javascript bridge only when the user is in the editing mode.
<!DOCTYPE html>
<html>
<head>
<title>{{ story.name }}</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i" rel="stylesheet">
<link rel="stylesheet" href="https://a.storyblok.com/t/43698/assets/css/above.css?1523887806">
</head>
<body>
{% include 'header' with blok: global.content %}
{% for blok in story.content.body %}
{% include blok.component with blok: blok %}
{% endfor %}
<script type="text/javascript" src="//app.storyblok.com/f/storyblok-latest.js?t= Dv2ok3DqODzzb8QUuN2XCgtt"></script>
<script type="text/javascript">
storyblok.on('change', function(event) {
if (!event.slugChanged) { location.reload(true) }
})
storyblok.on('published', function(event) {
if (!event.slugChanged) { location.reload(true) }
})
</script>
</body>
</html>
5. Create the first editable components
The demo content that get's created when adding a new space in Storyblok has already some components preconfigured. To make this components clickable in the editor the only thing that you need to do is to add {{ blok._editable }}
before a DOM element.
Let's create our first component templates _teaser.liquid
, _grid.liquid
and _feature.liquid
in the components
folder.
Create _teaser.liquid
{{ blok._editable }}
<section class="fdb-block bg-dark fdb-viewport"
style="background-image: url('//a.storyblok.com/f/43698/3000x2000/7696f16f6b/bg_c_1.svg')">
<div class="container align-items-center justify-content-center d-flex">
<div class="row justify-content-center">
<div class="text-center col-12 col-sm-10 col-md-8">
<h1>{{ blok.headline }}</h1>
<p class="mt-5">
<a class="btn " href="#">Call to action</a>
</p>
</div>
</div>
</div>
</section>
Create _grid.liquid
In this component we iterate over columns to include other components dynamically.
{{ blok._editable }}
<div class="container">
<div class="row text-center justify-content-center pt-5 pb-5">
{% for item in blok.columns %}
{% include item.component with blok: item %}
{% endfor %}
</div>
</div>
Create _feature.liquid
{{ blok._editable }}
<div class="col-12 col-sm-6 col-lg-3 pt-4 pt-lg-0">
<h3><strong>{{ blok.name }}</strong></h3>
<p>{{ blok.text }}</p>
</div>
At the end you should be able to see a teaser and three feature blocks that are clickable in Storyblok.
6. Create global components
To create global content like a header navigation you can define a new content type. Create a new "Story" name it "Global" and type setting
in the content type field.
Add nav_links
with type "Blocks" to the schema of the settings content type and then create nav_item blocks with name
and link
in the schema definition.
Extend the pages_controller.rb
After you have published the global content item add the api call to your controller:
...
assigns = {
story: client.story(params[:path])['data']['story'],
global: client.story('global')['data']['story']
}
...
Last step is to add the components to your Ruby on Rails project:
Add _header.liquid
{{ blok._editable }}
<header>
<div class="container">
<nav class="navbar navbar-expand-md">
<a class="navbar-brand" href="/">
<img src="//a.storyblok.com/f/43698/437x202/371b07400b/img_logo_new.png" height="30" alt="Logo">
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar-2651eb25-213b-4216-8d83-354812817602" aria-controls="navbar-2651eb25-213b-4216-8d83-354812817602" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbar-2651eb25-213b-4216-8d83-354812817602">
{% if blok.nav_links != blank %}
<ul class="navbar-nav mr-auto">
{% for item in blok.nav_links %}
{% include item.component with blok: item %}
{% endfor %}
</ul>
{% endif %}
</div>
</nav>
</div>
</header>
Add _nav_item.liquid
{{ blok._editable }}
<li class="nav-item{% if blok.link.cached_url contains request.path %} active{% endif %}">
<a class="nav-link" href="{{ blok.cached_url }}">
{{ blok.name }}
</a>
</li>
Conclusion
It's incredibly easy to build a flexible block based website with Ruby and Rails and Storyblok. In case you want to extend an existing project with a blog be sure to checkout the how to create blog content structure article.
Resource | Link |
---|---|
Github repository of this Tutorial | github.com/storyblok/rails-boilerplate |
Ruby on Rails | Ruby on Rails |
Storyblok App | Storyblok |