Building a static blog using Jekyll & Strapi
Published: 2022-11-15 10:39:14


If you are familiar with our [blog](https://strapi.io/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](https://strapi.io/blog/build-a-blog-with-react-strapi-and-apollo), [Next.js](https://strapi.io/blog/build-a-blog-with-next-react-js-strapi-and-apollo), [Vue](https://strapi.io/blog/build-a-blog-with-vue-strapi-and-apollo), [Nuxt.js](https://strapi.io/blog/build-a-blog-using-nuxt-strapi-and-apollo), or [Angular](https://strapi.io/blog/build-a-blog-with-angular-js-strapi-and-apollo).

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](https://jekyllrb.com/), [Hugo](https://gohugo.io/), [Gatsby](https://www.gatsbyjs.org/), [Hexo](https://hexo.io/), 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](https://github.com/jekyll/jekyll), [Jekyll](https://jekyllrb.com/) is the most famous static website generator.


![Jekyll logo](https://assets.strapi.io/uploads/jekyll-logo-2.png_491c0f8542.png)


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](https://pages.github.com/)!

## What is Strapi?

[Strapi](https://strapi.io/) 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](https://strapi.io/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](https://github.com/strapi/jekyll-blog). We will make this tutorial shorter and more efficient by using our [new templates](https://github.com/strapi?q=strapi-template&type=&language=).


## 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:

```bash
    mkdir blog-strapi
    cd blog-strapi
```

### Back-end Setup

That's the easiest part of this tutorial thanks to Rémi who developed a series of [Strapi templates](https://github.com/strapi?q=strapi-template&type=&language=) that you can use for your [Blog](https://github.com/strapi/strapi-template-blog), [E-commerce](https://github.com/strapi/strapi-template-ecommerce), [Portfolio](https://github.com/strapi/strapi-template-portfolio), or [Corporate](https://github.com/strapi/strapi-template-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](https://github.com/strapi/strapi-template-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.

```bash
      yarn create strapi-app backend --template blog
```

![](https://paper-attachments.dropbox.com/s_DA2F37D1650218B1E7CF39E3C28DC61E88BB6FD780D7F250A65122436D6796D2_1641684324232_Screen+Shot+2022-01-09+at+12.25.19+AM.png)


Don't forget that Strapi is running on [http://localhost:1337](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](https://github.com/strapi?q=starter&type=&language=) and before the [templates](https://github.com/strapi?q=strapi-template&type=&language=), 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](https://paper-attachments.dropbox.com/s_DA2F37D1650218B1E7CF39E3C28DC61E88BB6FD780D7F250A65122436D6796D2_1641684464313_Screen+Shot+2022-01-09+at+12.27.38+AM.png)



![Sample Screenshot](https://paper-attachments.dropbox.com/s_DA2F37D1650218B1E7CF39E3C28DC61E88BB6FD780D7F250A65122436D6796D2_1641684579491_Screen+Shot+2022-01-09+at+12.29.35+AM.png)



![Sample Screenshot](https://paper-attachments.dropbox.com/s_DA2F37D1650218B1E7CF39E3C28DC61E88BB6FD780D7F250A65122436D6796D2_1641684595648_Screen+Shot+2022-01-09+at+12.29.52+AM.png)



![Sample Screenshot](https://paper-attachments.dropbox.com/s_DA2F37D1650218B1E7CF39E3C28DC61E88BB6FD780D7F250A65122436D6796D2_1641684677940_Screen+Shot+2022-01-09+at+12.31.11+AM.png)


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:

```bash
     gem install jekyll bundler
```

> If you have any issue installing Jekyll, please take a look at the [Jekyll's documentation](https://jekyllrb.com/docs/troubleshooting/) or at [this issue](https://github.com/jekyll/jekyll/issues/6690#issuecomment-366207782).

Create a Jekyll `frontend` folder by running the following command:

```bash
      jekyll new frontend
```
Once the installation is complete, you can start the server:

```bash
     cd frontend
     bundle exec jekyll serve
    # => Now browse to http://localhost:4000
```
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](https://jekyllrb.com/docs/plugins). [Several plugins already exist](https://jekyllrb.com/docs/plugins/#available-plugins), 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](https://github.com/strapi/jekyll-strapi)!

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]
```

Install the gem by running the following command:

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


```
    ...
    theme: minima
    plugins:
      - jekyll-feed
      - jekyll-strapi
    ...
```
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
```
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

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](https://github.com/jekyll/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:

```html
    ---
    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>
```

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](https://assets.strapi.io/uploads/jekyll_blog_1_165fa8d1f5.png)



## 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
```
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:

```bash
      export API_URL=http://localhost:1337/api
```
Let's create the template and define the content displayed in a new file named `article.html` located in the `_layouts` directory:

```html
    ---
    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 }}
```

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
```
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](https://assets.strapi.io/uploads/jekyll_blog_4_6b9b4b5aed.png)



## 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:

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

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
```

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

```html
    ---
    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>
```

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

Finally, restart the server and visit any category page.


![Links to Categories](https://assets.strapi.io/uploads/jekyll_blog_2_414a990f07.png)



![Category page](https://assets.strapi.io/uploads/jekyll_blog_3_b4b64db6e1.png)



## 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.