Skip to main content

Add a CMS to Ruby on Rails in 5 minutes

    Try Storyblok

    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

    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.

    Storyblok Bridge

    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 =
          logger: logger,
          token: 'YOUR_TOKEN',
          version: 'draft'
        assigns = {
          story: client.story(params[:path])['data']['story']
        Liquid::Template.file_system ='app/views/components')
        template = Liquid::Template.parse('app/views/layouts/page.liquid'))
        render html: template.render!(assigns.stringify_keys, {}).html_safe

    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

    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>
      <title>{{ }}</title>
      <link rel="stylesheet" href="" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
      <link href=",100i,300,300i,400,400i,500,500i,700,700i,900,900i" rel="stylesheet">
      <link rel="stylesheet" href="">
      {% include 'header' with blok: global.content %}
      {% for blok in story.content.body %}
        {% include blok.component with blok: blok %}
      {% endfor %}
      <script type="text/javascript" src="// 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) }

    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('//')">
      <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>

    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 %}

    Create _feature.liquid

    {{ blok._editable }}
    <div class="col-12 col-sm-6 col-lg-3 pt-4 pt-lg-0">
      <h3><strong>{{ }}</strong></h3>
      <p>{{ blok.text }}</p>

    At the end you should be able to see a teaser and three feature blocks that are clickable in Storyblok.

    Ruby on Rails CMS Screenshot

    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.

    Global Ruby on Rails component

    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 }}
      <div class="container">
        <nav class="navbar navbar-expand-md">
          <a class="navbar-brand" href="/">
          <img src="//" height="30" alt="Logo">
          <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>
          <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 %}
            {% endif %}

    Add _nav_item.liquid

    {{ blok._editable }}
    <li class="nav-item{% if contains request.path %} active{% endif %}">
      <a class="nav-link" href="{{ blok.cached_url }}">
        {{ }}


    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
    Ruby on Rails Ruby on Rails
    Storyblok App Storyblok