DEV Community

Strapi
Strapi

Posted on • Originally published at strapi.io

Building a static blog using Jekyll & Strapi

If you are familiar with our blog, you must have seen that we've released a series of tutorials to make blogs using Strapi with a variety of frontend frameworks: React, Next.js, Vue, Nuxt.js, or Angular.

A static website contains web pages with fixed content. Technically, it is a simple list of HTML files, which displays the same information to every visitor. Unlike dynamic websites, they do not require any back-end programming or database. Publishing a static website is easy: the files are uploaded on a simple Web server or storage provider. The two main advantages of static websites are security and speed: there is no database so it can not be hacked and there is no need to render a page for each request, which makes Web browsing faster.

To make their creation easier, numerous open-source static websites generators are available: Jekyll, Hugo, Gatsby, Hexo, etc. Most of the time, the content is managed through static (ideally Markdown) files or a Content API. Then, the generator requests the content, injects it in templates defined by the developer and generates a bunch of HTML files.

What is Jekyll?

With more than 34,000 stars on GitHub, Jekyll is the most famous static website generator.

Jekyll logo

Developed on top of Ruby, Jekyll makes static website creation super easy. It includes many features such as permalinks, categories, pages, and custom layouts. Write some Markdown files, customize your templates and you will get an easy-to-host website in seconds.

Last but not least, Jekyll is the static website generator powering GitHub Pages!

What is Strapi?

Strapi is the open-source Headless CMS. It saves weeks of API development time and allows easy long-term content management through a beautiful administration panel anyone can use.

Unlike other CMSs, Strapi is 100% open-source, which means:

  • Strapi is completely free.
  • You can host it on your servers, so you own the data.
  • It is entirely customizable and extensible, thanks to the plugin system.

Try a live demo

Goal

The goal here is to be able to create a simple static blog website using Strapi as the backend and React for the frontend. The source code is available in this repository. We will make this tutorial shorter and more efficient by using our new templates.

Prerequisites

This tutorial will always use the latest version of Strapi. That is awesome right!? You'll understand why below. You need to have node v.12 installed and that's all.

Setup

Create a blog-strapi folder and cd into it by running the following commands:

    mkdir blog-strapi
    cd blog-strapi
Enter fullscreen mode Exit fullscreen mode

Back-end Setup

That's the easiest part of this tutorial thanks to Rémi who developed a series of Strapi templates that you can use for your Blog, E-commerce, Portfolio, or Corporate website project.

These templates are Strapi applications containing existing collection types and single-types suited for the appropriate use case, and data.

In this tutorial, we'll use the Blog template and connect a static Jekyll website to it.

Note: for this tutorial, we will be using yarn as our package manager.

Create your Strapi backend folder using the Blog template.

      yarn create strapi-app backend --template blog
Enter fullscreen mode Exit fullscreen mode

Don't forget that Strapi is running on http://localhost:1337. Create your admin user by signing up!

That's it! You're done with Strapi! I'm not kidding, we can start to create our Jekyll website now in order to fetch our content from Strapi. But before that, let's talk about this amazing template you just created.

You should know that before the starters and before the templates, we only had tutorials. The idea of creating starters came to us when we realized that we could do something with the end result of our tutorials. Thus were born our starters.

However, Strapi evolves quickly, very quickly and at the time the starters constituted a repository including the backend as well as the frontend. This means that updating the Strapi version on all our starters took time, too much time. We then decided to develop templates that are always created with the latest versions of Strapi. Quite simply by passing in parameter the repository of the desired template like you just did. Also, it gives you a good architecture for your Strapi project.

These templates provide a solid basis for your Strapi application:

  • 3 Collection types are already created - Article, Category, Writer
  • 2 Single types are already created - Global, Homepage
  • Find and FindOne permissions are publicly open for all of your content-types types and single types
  • Existing data

Sample Screenshot

Sample Screenshot

Sample Screenshot

Sample Screenshot

Feel free to modify all of this set-up according to your needs.

Nice! Now that Strapi is ready, you are going to create your Jekyll application.

Front-end Jekyll setup

The easiest part has been completed. Let's get our hands dirty developing our blog!

Requirements

  • Ruby version 2.2.5 or above, including all development headers (ruby installation can be checked by running ruby -v)
  • RubyGems (which you can check by running gem -v)
  • GCC and Make (in case your system doesn’t have them installed, which you can check by running gcc -v,g++ -v and make -v in your system’s command-line interface)
  • First, install the Jekyll CLI:
     gem install jekyll bundler
Enter fullscreen mode Exit fullscreen mode

If you have any issue installing Jekyll, please take a look at the Jekyll's documentation or at this issue.

Create a Jekyll frontend folder by running the following command:

      jekyll new frontend
Enter fullscreen mode Exit fullscreen mode

Once the installation is complete, you can start the server:

     cd frontend
     bundle exec jekyll serve
    # => Now browse to http://localhost:4000
Enter fullscreen mode Exit fullscreen mode

When you manage a static website, your data can come from different sources: Markdown files, CSV files, a WordPress website (using the JSON REST API plugin), etc.

By default, Jekyll uses Markdown files to manage data. Fortunately, thanks to the plugin system, you can get data from any source.

To connect Jekyll to a new source of data, you have to develop a new plugin. Several plugins already exist, so one of them should fill your needs.

In this example, we are using Strapi. We are going to need a plugin compatible with Strapi APIs. Good news: we built it for you!

Add the jekyll-strapi gem to your Gemfile:

    source "https://rubygems.org"
    # Hello! This is where you manage which Jekyll version is used to run.
    # When you want to use a different version, change it below, save the
    # file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
    #
    #     bundle exec jekyll serve
    #
    # This will help ensure the proper Jekyll version is running.
    # Happy Jekylling!
    gem "jekyll", "~> 4.2.0"
    # This is the default theme for new Jekyll sites. You may change this to anything you like.
    gem "minima", "~> 2.5"
    gem "jekyll-strapi"
    # If you want to use GitHub Pages, remove the "gem "jekyll"" above and
    # uncomment the line below. To upgrade, run `bundle update github-pages`.
    # gem "github-pages", group: :jekyll_plugins
    # If you have any plugins, put them here!
    group :jekyll_plugins do
      gem "jekyll-feed", "~> 0.12"
    end

    # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
    # and associated library.
    platforms :mingw, :x64_mingw, :mswin, :jruby do
      gem "tzinfo", "~> 1.2"
      gem "tzinfo-data"
    end

    # Performance-booster for watching directories on Windows
    gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
Enter fullscreen mode Exit fullscreen mode

Install the gem by running the following command:

      bundle install
Enter fullscreen mode Exit fullscreen mode

Then add jekyll-strapi to your plugins list in the _config.yml file:

    ...
    theme: minima
    plugins:
      - jekyll-feed
      - jekyll-strapi
    ...
Enter fullscreen mode Exit fullscreen mode

This plugin needs some configuration. At the end of the _config.yml file, add the following code:

    theme: minima
    plugins:
      - jekyll-feed
      - jekyll-strapi

    strapi:
        # Your API endpoint (optional, default to http://localhost:1337/api)
        endpoint: http://localhost:1337/api
Enter fullscreen mode Exit fullscreen mode

Now we want to display the list of articles. To do so, we are going to instruct Jekyll to retrieve the list of articles from Strapi:

strapi:
    # Your API endpoint (optional, default to http://localhost:1337/api)
    endpoint: http://localhost:1337/api
    collections:
        articles:
            # Collection name (optional). Used to construct the url requested. Example: type `foo` would generate the following url `http://localhost:1337/api/foo`.
            type: articles
Enter fullscreen mode Exit fullscreen mode

Each content type must be listed in the collections object. The type field is used to build the URL requested. For example, if your type is Articles, the requested URL will be [http://localhost:3000/api/articles](http://localhost:3000/api/articles). The permalink is the URL structure you want to use for your blog posts. We could have chosen /api/articles/:id, but slugs are more friendly.

After that, we need to display the data. Jekyll uses a theme system. By default, the used theme is minima. The theme's files are not generated in the Jekyll projects, but you can override every theme's template by creating a file with the same name in the _layouts folder.

To replace the existing home page content, create the folder _layouts with home.html inside it:

    ---
    layout: default
    ---

    <div class="home">
        <h1 class="page-heading">Articles</h1>
        {%- if strapi.collections.articles.size > 0 -%}
        <ul class="article-list">
            {%- for article in strapi.collections.articles -%}
            <li>
                <span class="article-meta">{{ article.publishedAt | date_to_string }}</span>
                <h3>
                    <a class="article-link" href="/articles{{ article.slug | relative_url }}">
                        {{ article.title }}
                    </a>
                </h3>
                <!-- Display an excerpt of the article -->
                <p>{{ article.content | markdownify | strip_html | truncatewords: 10 }}</p>
            </li>
            {%- endfor -%}
        </ul>
        {%- endif -%}
    </div>
Enter fullscreen mode Exit fullscreen mode

Finally, reload your app to see the changes.

Explanations

  • At the top of the file, we indicate the layout that we want to use to display the header and the footer.
  • The plugin jekyll-strapi exposes the data received from the API through the global variable strapi, which is accessible from every template. To display the list of posts, we checked that the list strapi.collections.articles is not empty. If it is the case, we loop on it to display each item.
  • As you can see, some fields are displayed using |. The vertical line symbol is used by Jekyll's filters, which alter the way to display text. For example, article.publishedAt uses the data_to_string to display the post's creation date in a more human friendly way.
  • Every entry includes an additional property named url which represents the url of the record's page based on the permalink given in the configuration.

Artciles

Article Pages

Our website now starts looking like a blog which is a good thing. However, an important part is still missing: the article's details page.

First, we want to be sure to display images. and to do this we need to use env variable to fetch the url of the Strapi project depending on the environment.

Create a _plugins/env.rb file containing the following:

    module Jekyll
      class EnvVariables < Generator
        def generate(site)
          site.config['env'] = {}
          ENV.each_pair { |k, v| site.config\['env'\][k] = v }
        end
      end
    end
Enter fullscreen mode Exit fullscreen mode

This will fetch all your env variable and make them available in site.env.

Create an API_URL env variable on your machine by running the following command and then restart the server:

      export API_URL=http://localhost:1337/api
Enter fullscreen mode Exit fullscreen mode

Let's create the template and define the content displayed in a new file named article.html located in the _layouts directory:

    ---
    layout: default
    ---

    <img src="{{ site.env.API_URL | append: page.document.image.url }}">
    <h1></h1>
    <span class="post-meta">{{ page.document.publishedAt | date_to_string }}</span>
    {{ page.document.content | markdownify }}
Enter fullscreen mode Exit fullscreen mode

The template displays the title of the article, some metadata and converts the content from Markdown to HTML.

In the _config.yml file, we must add layout and output lines to indicate to Jekyll that we want to generate a new page for each article, using the template we created previously:

    strapi:
        # Your API endpoint (optional, default to http://localhost:1337/api)
        endpoint: http://localhost:1337/api
        collections:
            articles:
                # Collection name (optional). Used to construct the URL requested. Example: type `foo` would generate the following url `http://localhost:1337/api/foo`.
                type: articles
                permalink: /articles/:slug/
                layout: article.html
                output: true
Enter fullscreen mode Exit fullscreen mode

After restarting the Jekyll server, you should be able to see a new folder named articles, in the _site folder, containing the generated pages.

From now on, you can visit the detail page by clicking on URLs displayed on the homepage.

Article page

Category View

Articles belong to a category. Let's create a page per category.

Create a _layouts/category.html file and add the following code to it:

    ---
    layout: default
    ---

    <h1>{{ page.document.name }}</h1>

    {%- if page.document.articles.size > 0 -%}
    <ul class="article-list">
        {%- for article in page.document.articles -%}
        <li>
            <span class="article-meta">{{ article.publishedAt | date_to_string }}</span>
            <h3>
                <a class="article-link" href="/articles{{ article.slug | relative_url }}">
                    {{ article.title }}
                </a>
            </h3>
            <!-- Display an excerpt of the article -->
            <p>{{ article.content | markdownify | strip_html | truncatewords: 10 }}</p>
        </li>
        {%- endfor -%}
    </ul>
    {%- endif -%}
Enter fullscreen mode Exit fullscreen mode

Add the collection type in your _config.yml file:

    strapi:
        # Your API endpoint (optional, default to http://localhost:1337)
        endpoint: http://localhost:1337
        collections:
            # Example for a "posts" collection
            articles:
                # Collection name (optional). Used to construct the url requested. Example: type `foo` would generate the following url `http://localhost:1337/foo`.
                type: articles
                permalink: /articles/:slug/
                layout: article.html
                output: true

            categories:
                # Collection name (optional). Used to construct the url requested. Example: type `foo` would generate the following url `http://localhost:1337/foo`.
                type: categories
                permalink: /categories/:name
                layout: category.html
                # Generate output files or not (default: false)
                output: true
Enter fullscreen mode Exit fullscreen mode

Update the _layouts/home.html file with the following code:

    ---
    layout: default
    ---

    <div class="home">
        <h1 class="page-heading">Articles</h1>
        {%- if strapi.collections.articles.size > 0 -%}
        <ul class="article-list">
            {%- for article in strapi.collections.articles -%}
            <li>
                <span class="article-meta">{{ article.publishedAt | date_to_string }}</span>
                <h3>
                    <a class="article-link" href="/articles{{ article.slug | relative_url }}">
                        {{ article.title }}
                    </a>
                    -
                    <a href="/categories/{{ article.category.name | relative_url }}">
                      ({{ article.category.name}})
                    </a>
                </h3>
                <!-- Display an excerpt of the article -->
                <p>{{ article.content | markdownify | strip_html | truncatewords: 10 }}</p>
            </li>
            {%- endfor -%}
        </ul>
        {%- endif -%}
    </div>
Enter fullscreen mode Exit fullscreen mode

This includes a link to the category an article belongs to.

Finally, restart the server and visit any category page.

Links to Categories

Category page

Conclusion

Huge congrats, you have successfully finished this tutorial. I hope you enjoyed it.

Feel free to add additional features, adapt this project to your own needs, and give your feedback in the comment section below.

Top comments (0)