Add a headless CMS to Ember in 5 minutes

Contents
    Try Storyblok

    Storyblok is the first headless CMS that works for developers & marketers alike.

    In this short tutorial we show you how to integrate the Storyblok API into an Ember App. We build the components step by step and develop the integration using Storyblok API and Storyblok SDK. This is the final result:

    You can clone this tutorial at https://github.com/storyblok/storyblok-ember-boilerplate

    Environment Setup

    Requirements

    • Understanding of Ember

    • Node, yarn (or npm) and npx installed

    • An account on Storyblok to manage content

    • A space already configured in Storyblok

    Setup the project

    We use ember-cli to generate the initial setup for our project and to generate the routes, components and other things. To generate the initial project, execute the new command:

    # generate the project
    ember new storyblok-test
    
    # install ember-fecth addon
    ember install ember-fetch

    This command will config the Ember application in storyblok-test folder and install ember-fecth package.

    The next step is to load all of the data from Storyblok API into your Ember application. For this, it is necessary to create a index route:

    ember g route index

    After this, edit the app/router.js file to map the index route as a dynamic route with path param like this:

    // route.js
    Router.map(function() {
      this.route('index', { path: '/:path' })
    });

    The index route will receive a path as a parameter. This is useful when we get data from the Storyblok API

    Creating a service to connect the Storyblok API

    Start by creating a service to put in a Storyblok API connection logic. Execute the generate service command:

    ember g service storyblok

    This command creates a storyblok.js file into the app/services folder. Then edit it:

    import Service from '@ember/service';
    import fetch from 'ember-fetch/ajax';
    
    export default Service.extend({
      init() {
        this._super(...arguments);
    
        const token = '<YOUR_SPACE_TOKEN>'
    
        this.config = {
          token,
          version: 'draft'
        }
      },
    
      loadStory(path) {
        const config = this.get('config')
    
        const URL = `https://api.storyblok.com/v1/cdn/stories/${path}?token=${config.token}&version=${config.version}`
    
        return fetch(URL)
          .then(response => response.story)
          .catch(err => {
            console.log(err) // eslint-disable-line
          });
      },
    });

    With the service created, edit the app/routes/index.js file to get this path parameter and use it to load data from Storyblok API:

    import Route from '@ember/routing/route';
    import { inject as service } from '@ember/service';
    
    export default Route.extend({
      storyblok: service('storyblok'),
    
      model(params) {
        const { path } = params
    
        return this.storyblok.loadStory(path)
      }
    });

    The model function is executed before the route loads and puts the content in the route template. In that case, it will load a specific story from Storyblok API and put it in the template.

    Checking what we've done so far

    To know if the data is loading correctly, execute the Ember serve command on the terminal using npm or yarn:

    yarn start # npm start

    Open your browser in http://localhost:4200/home and you should see the Ember welcome page. Modify this to show the page name.

    Edit the app/templates/application.hbs by removing all content less than {{outlet}} expression. After that, edit the index.hbs file to show the page name in the h1 tag.

    {{!-- application.hbs file --}}
    <h1>{{this.model.name}}</h1>

    Open your browser again in http://localhost:4200/home. You should see the 'Home' name like this:

    Setup the Storyblok components

    Now create some components to render the home page correctly.

    Teaser component

    First, create the component files by using the Ember generate command:

    ember g component teaser

    Then, edit the teaser.hbs file to this:

    <div class="teaser">
      {{this.blok.headline }}
    </div>

    Grid component

    Repeat what was done with the teaser component: execute the generate command and edit the grid.hbs file:

    ember g component grid

    Edit the grid component using the component helper to render the correct component that came from the Storyblok API

    <div class="grid">
      {{#each this.blok.columns as |data|}}
        {{component data.component blok=data}}
      {{/each}}
    </div>

    Feature component

    The last component (for now) is the feature component. Execute in your terminal:

    ember g component feature

    Edit the feature.hbs file:

    <div class="column feature">
      {{this.blok.name}}
    </div>

    Editing the index.hbs route

    Finally, edit the index.hbs file to iterate over our components and render them correctly:

    <div class="application">
      {{#each this.model.content.body as |blok|}}
        {{component blok.component blok=blok}}
      {{/each}}
    </div>

    Adding some styles

    Before seeing what it looks like, try adding some styles. Add this CSS link in the app/index.html file and edit the app/styles/app.css to it:

    * {
      box-sizing: border-box;
    }
    
    .application {
      max-width: 760px;
      margin: 0 auto;
      padding-left: 20px;
      padding-right: 20px;
    }
    
    .grid .ember-view {
      flex: 1;
    }
    
    .grid .ember-view .feature {
      width: auto;
      text-align: center;
    }

    Open your browser again in http://localhost:4200/home:

    About page

    Create a new page called 'About' in our Storyblok space. Then add two components on schema: a Teaser component and a Markdown component. The last one will be a Richtext component with a simple Lorem Ipsum text.

    Creating a Navbar component

    Before setting up the About page in this project, create a Navbar component to switch between pages. Execute this command in your terminal:

    ember g component navbar

    Edit the navbar.hbs file like this:

    <ul class="navbar">
      <li class="navbar__item">
        <a href="/home"> Home </a>
      </li>
    
      <li class="navbar__item">
        <a href="/about"> About </a>
      </li>
    </ul>

    Add this component in the index.hbs file:

    <div class="application">
      <Navbar />
    
      {{#each this.model.content.body as |blok|}}
        {{component blok.component blok=blok}}
      {{/each}}
    </div>

    And lastly, add some styles in app.css replacing the content with the following:

    * {
      box-sizing: border-box;
    }
    
    ul {
      margin: 0;
      padding: 0;
    }
    
    li {
      list-style: none;
    }
    
    .application {
      max-width: 760px;
      margin: 0 auto;
      padding-left: 20px;
      padding-right: 20px;
    }
    
    .grid .ember-view {
      flex: 1;
    }
    
    .grid .ember-view .feature {
      width: auto;
      text-align: center;
    }
    
    .application .navbar {
      display: flex;
      justify-content: flex-end;
      margin: 20px 0;
    }
    
    .application .navbar .navbar__item:not(:first-of-type) {
      margin-left: 10px;
    }

    Now open your browser and check if the Home page looks like this:

    Go to the About page by clicking the About link in navbar... What do you see? Hopefully you see the Navbar links and an About title. But where is the Loren Ipsum text from Markdown? If you open the console's browser there will be an error message saying that the 'markdown' component name does not exist. Let's fix this!

    Set up the Richtext component

    You can find more information about the Rich Text component and how to you use it in our documentation. For now, use our Javascript SDK with the resolver to transform the complex JSON structure from Storyblok into HTML that we will use for our page.

    Execute the generate component command in our terminal:

    ember g component markdown

    Install the Storyblok Javascript SDK:

    yarn add storyblok-js-client # npm install storyblok-js-client

    Edit the markdown.js file creating an Ember file that includes the transformations that we need.

    import Component from '@ember/component';
    import { computed } from '@ember/object';
    import { inject as service } from '@ember/service';
    
    export default Component.extend({
      storyblok: service('storyblok'),
    
      htmlData: computed('blok', function() {
        const blok = this.get('blok');
        return this.storyblok.richtextResolver(blok.content);
      })
    });

    Finally, edit the markdown.hbs file to show that the HTML has been transformed.

    {{{this.htmlData}}}

    You see that we use the Storyblok service that we created before. Now, replace the storyblok.js file with the following content:

    import Service from '@ember/service';
    import fetch from 'ember-fetch/ajax';
    import StoryblokClient from 'storyblok-js-client';
    
    export default Service.extend({
      init() {
        this._super(...arguments);
    
        const token = '<YOUR_SPACE_TOKEN>'
    
        this.config = {
          token,
          version: 'draft'
        }
    
       // initialize the SDK
        this.client = new StoryblokClient({
          accessToken: token
        })
      },
    
      loadStory(path) {
        const config = this.get('config')
    
        const URL = `https://api.storyblok.com/v1/cdn/stories/${path}?token=${config.token}&version=${config.version}`
    
        return fetch(URL)
          .then(response => response.story)
          .catch(err => {
            console.log(err) // eslint-disable-line
          });
      },
    
      // we add this function to use the resolver function from SDK
      richtextResolver(content) {
        return this.client.richTextResolver.render(content)
      }
    });
    

    Open your browser in http://localhost:4200/about and see the correct content:

    Conclusion

    In this tutorial you learned how to integrate Storyblok API in an Ember framework. You created some components and used the Ember Service to integrate the Storyblok data into an Index route. You can clone this tutorial at https://github.com/storyblok/storyblok-ember-boilerplate too.