Academic
Blog
Analytics

Algolia search engine in Jekyll static pages

5 mins read time ❤️ Views


Static webpages are very popular these days, they are used to deploy showcase websites that contains only html and don’t need a backend server.

The lack of backend server simplifies a lot the infrastructure to deploy the website and now a days you can deploy for free these kinds of pages in places like github, gitlab, netlify and more.

This is what I have done to write this blog, which lead me to write more than 30 articles until know. Having a lot of information also implies a need to organised them, which I have done using categories and tags but it has been difficult to search for a particular article just by having the categories.

So I decided to add a search engine to my blog using Algolia API.


This article shows how to combine Algolia search API with Jekyll and Github Actions

Search engine for static webpages

There are two ways to implement a search engine:

  • having a backend server that fetch the data and calculates the recommendation or
  • using a frontend application to filter data on the client side.

The advantage of using a frontend application to filter data is the fact that you don’t need a backend service, so it works well with static sites. However, if your data is heavy, it may decrease the web navigation performance. A popular example of search engine on client side is lunr.js.

The backend service is then a more robust solution that can scale with your data. Some examples of engines that can be used for backend search engines are Apache SolR or Elasticsearch.

I wanted to have the advantages of both of them, having an scalable, fast and light search solution, so I decided to use Algolia search engine.

Why Algolia ?

Algolia offers a hosted search engine capable of delivering real-time results from the first keystroke. You can load data in text and numerical format, Algolia indexes data in a backend service. In addition Algolia also provides an API to query your data and obtain relevant results in under 100ms anywhere in the world.

You don’t have to worry about hosting an backend server and you can fetch data in the frontend in a fast and scalable way.

There are two ways to communicate with Algolia:

  1. the backend: to push your data to Algolia service.
  2. the frontend: the php or javascript libraries that is executed in the client side to fetch your search results.

Algolia Jekyll

For the backend service, in a static website context, most applications provide utilities to push your records to Algolia using Hugo, Jekyll or Pelican. In Jekyll, you can install the package jekyll-algolia by adding the following to your Gemfile:

# Gemfile

group :jekyll_plugins do
  gem 'jekyll-algolia', '~> 1.0'
end

Then download all the dependencies using bundle installand push information about your html files using the command bundle exec jekyll algolia. This package sends the structured data from your html/md like title, description, image, categories, tags, content, etc.

You can customize the information that you want to send in your _config.yml file as stated in the jekyll-algolia documentation. For example, I exclude pages from my home page and drafts.

Github Actions for Algolia CI

Even if pushing data to Algolia is very simple with the jekyll plugins, there is nothing like having a continuous integration service that do it automatically for you.

Since my blog is already in github, I chose Github Actions to leverage this action for me. I just have to create the following yaml in the .github/workflows folder:

# .github/workflows/jekyll-algolia.yml
on: [push]

jobs:
  build:
    name: Algolia push records

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v1
      - name: Set up Ruby 2.6
        uses: actions/setup-ruby@v1
        with:
          ruby-version: 2.6.x
      - name: Install dependencies and push records
        run: |
          gem install bundler
          bundle install --jobs 4 --retry 3
          bundle exec jekyll algolia
        env:
          ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}

As you can see it this will install the dependencies and launch the command to push the data to Algolia. Please note that the ALGOLIA_API_KEY needs to be defined in the github secrets.

Frontend application

Algolia offers multilingual implementation to query their API. I chose the vanilla instantsearch.js to adapt the elements with the bulma css that I use in my website.

In order to add a functional search bar I need to put an input and hits elements in the html page and add a javascript function to render them:

// Listen changes from input element
search.addWidget({
  init: function(opts) {
    const helper = opts.helper;
    const input = document.querySelector('#searchBox');
    input.addEventListener('input', function(e) {
      helper.setQuery(e.currentTarget.value) // update the parameters
            .search(); // launch the query
    });
  }
});

// Render results in hits element
search.addWidget({
  render: function(opts) {
    const results = opts.results;
    // read the hits from the results and transform them into HTML.
    document.querySelector('#hits').innerHTML = results.hits.map(function(h) {
      let formattedTime = date_unix_str(h.date);
      let external_tag = ('link' in h) ? `<span class="tag is-danger"><i class="fas fa-external-link-alt"></i></span>`: '' ;
      let img_template = '';
      if ('image' in h) {
        img_template = `
      <div class="card-image">
        <figure class="image">
          <a href="${('link' in h) ? h.link : h.url}">
            <center>
              <amp-img src="${h.image.path}" width="368" height="245" alt="${h.title}" layout="intrinsic"></amp-img>
            </center>
          </a>
        </figure>
      </div>`
      } 

      return `
    <div class="column is-one-third">
      <div class="card">` + img_template  + `
        <div class="card-content">
          <div class="media apretaito">
            <div class="media-content">
              <a href="${('link' in h) ? h.link : h.url}" ${('link' in h) ? "target=\"_blank\"" : ''} class="title is-4">${h.title}</a>
            </div>
          </div>
          <div class="content apretaito">
            <p>
              ${h.description}
            </p>
            <div class="tags has-addons">
              <span class="tag">
                <i class="fas fa-calendar-alt"></i>&nbsp;${ formattedTime }
              </span>
              <span class="tag is-link">
                ${ h.categories.join(", ")}
              </span>` + external_tag + `
            </div>
          </div>
        </div>
      </div>
    </div>`
    }).join('');
  }
});

The rest of the code can be found at the github page of my website. There, you can find some extra functionalities like pagination buttons.

Instantsearch.js queries the API at every key stroke, which gives the impression of having the results on real time. You can try it at cristianpb.github.io/blog.


The search bar returns results in real time

Conclusions

Searching for content in my blog is now easy thanks to Algolia search engine and it’s free for the moment. I have a developer account which lets me save 10k records and do 50k indexing operations. Today I have 35 post entries, which results in 320 records ~= 3% of record utilisation. So I think I have plenty of time to use Algolia in my blog.

Github Actions automatically syncs records to Algolia at every push. Even if this can be done with other services like CircleCI or TravisCI, I prefer to keep all in one service.

One inconvenient that I have found is the fact that using instantsearch.js breaks AMP compatibility because AMP doesn’t allow to have custom javascript code in the webpage. For information AMP standard are some Google norms that certifies that your page is optimized, you can find more information about AMP in one of my other articles.

In relation with 🏷️ jekyll, algolia, github-actions, bulma:

Writing notes with Vimwiki and Hugo static generator

Vim is a simple and ubiquitous text editor that I use daily. In this article I show how to use Vim to take and publish diary notes using Vimwiki and Hugo.

Muse: Mopidy web client with Snapcast support

Services like Mopidy and Snapcast are ideal to create a multiroom streaming audio player using devices like the RaspberryPi or android telephones. This posts presents a web interface that uses the state of the art web technologies and integrates nicely with Mopidy and Snapcast.

Search posts functionality in AMP static website make with Jekyll

Good web reference is related with how fast your web page loads. Google works in a web format called AMP, which makes loading pages faster but also imposes some restrictions, like no external javascript. It's not simple to add API calls in AMP so this post shows how to implement a search fonctionality in a static page.

Static AMP website creation with Bulma and Jekyll

This posts shows how to include bulma css classes inside a jekyll website but keeping good performance from AMP websites

Subscribe to my newsletter 📰

and share it with your friends: