Using Jekyll as a static website generator in 2024

In a previous article I introduced the world of static website generators, explaining the reasons that pushed me to adopt this solution in general and Jekyll in particular.

In this article, I am going to show how my setup changed since then: what I removed from my plugin list, what I added, what I developed myself, and how I improved my workflow.

Is Jekyll a thing in 2024?

Some people argue that lately the number of commits, in the Jekyll Github repository, decreased, and that might be a symptom of a possible abandonment or a loose of interest in the project itself.

I am going to base my personal opinion on some facts:

  1. The Jekyll project at its 4th major release reached a certain stability and has been widely proven to be a good solution;
  2. It is a project used by GitHub itself to create the Github pages;
  3. Every project can stall for a certain time, and still ensure a minimum level of patching release before adding more complex changes in the code;
  4. The authors might often take some time to understand how to improve the project or refactor the code instead of adding features that should be reworked again later.

That said the Jekyll project looks to me alive and well supported, but to those still in doubt about adopting it or not, let’s say that at the maximum risk, the precious data (articles, images, YAML data, …), can easily be adapted to any other generator platform out there, being only text or structured text files involved.

My customizations

The list of customizations I adopted, evolved especially in the last year.

In the first phase, I was in a hurry and I stuffed everything I needed in the list only to make the job done, then I started to realize which one was useful, which other could be replaced, and which one had to be removed.

I also created some plugins myself to collect one or more solutions at once, and last but not least I started to measure performances to reduce the generation time.

Let’s proceed for every area I touched, how, and why.

Theme development

The present theme is built on top of the Foundation framework, but instead of installing it from the gem package, I decided to integrate Yarn, the NodeJS package manager successor of Bower.

The reason I prefer this method is that most of the time these frameworks are quicker available and updated as a NodeJS package than a Rubygem package.

As a plus, you can automatically download and store the packages under a subfolder of the Jekyll project just creating a .yarnrc file under the main folder with something like this:

--modules-folder "./assets/_vendor"

Of course, this last feature can be achieved with Bundler too:

$ bundle install --path ./assets/_vendor GEMNAME

Anyway I preferred to keep detached the Ruby side from the Javascript/SASS side and handle them with a different tool.

Having the sources in a project subfolder allows you to better study the related code especially if you want to apply heavy customizations through SASS variables and mixin.

So after installing NodeJs from the package manager or through nvm (an rvm clone), just type:

$ npm install --global yarn

Being sure that the .yarnrc file is at its place within the Jekyll project’s folder, from the same folder we can install our favorite framework, Foundation in my case:

$ yarn add foundation-sites

Of course, Jekyll must know where to look for the SASS files, so these changes must be added to the _config.yml file:

sass:
  sass_dir: assets/_vendor
  style: compressed

Just a little note about the folder path: I am using the regular assets folder, but the _vendor subfolder is prefixed by an underscore _, because:

  1. I am using the vanilla Jekyll feature to manage the generation of CSS files from the SASS files as stated in the _config.yml file;
  2. all the subfolders of _vendor won’t be copied under the _site/assets path because of its prefix;
  3. enabling the compression will remove the comments and useless spaces, reducing the bandwidth used.

I highly recommend building a theme upon an HTML framework to ensure a certain maintenance and usage of the modern CSS style techniques, without forgetting the grid system and the advantage that their implementation gives in terms of organization and mixin methods provided.

RecommendedAlternative
Custom themePick one
YarnBundler
FoundationBootstrap

Administration

Although to work on a Jekyll project you only need a terminal and a text editor, there are also some interesting plugins which might come in handy for the backend side:

Jekyll compose
adds some other commands to the jekyll command to, publish, rename create a draft, or unpublish a post, create a page or compose any other file belonging to a collection.
Jekyll admin
This is a true admin interface that’s started together with the serve command and allows to achieve all those operations needed to edit, delete, and publish content.
RecommendedAlternative
Jekyll-composeManually create the files
Jekyll-adminUse a text editor

SEO

With Search Engine Optimization we include all the techniques that help any web crawler to collect and assign the content of our pages to certain keywords handled internally by the search engine itself.

This is achieved with the help of dedicated plugins and the respect of some good practices.

Jekyll Seo Tag plugin is intended to create the meta tags in the HEAD section, and beside the standard tags (title, meta-description, meta-canonical-URL), also:

Some of the good practices, also in conjunction with the aforementioned plugin, involve:

  • A description field, to declare in the front matter, of max 160 characters;
  • use an authors.yaml data file to specify the name and twitter fields for each author to correctly fill the twitter:creator and twitter:author metadata, and an author field in the front matter as stated in the documentation;
  • a title property must be specified for each anchor;
  • every page must have an h1 tag;
  • the img elements must have the alt property setup;
  • every post that contains a cover image should insert that field in the front matter, so the SEO plugin can use it inside the related Twitter card tag.

Besides that, as extra features, I added:

RecommendedAlternative
jekyll-seo-tagManually create an include file
jekyll-disqus-pluginManually create an include file
jekyll-sitemapManually create the layout
jekyll-share-pluginManually create an include file

Metrics

Every blogger needs to know some statistics to understand whether or not his going in the right direction.

The tools used in this category aren’t always welcome because of some privacy issues, that’s why must associate some GDPR messages to warn the user about the data usage.

Of course, other paid alternatives offer similar or more services, but for what I need Google Analytics is more than enough, so I won’t go deeper into that topic because isn’t exactly my matter.

Just like similar tools, including the code below before the </body> tag and set up the google_analytics_id key in the _config.yml file is just enough:

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js', 'ga');
  ga('create', '{{site.config.google_analytics_id}}', 'auto');
  ga('send', 'pageview');
</script>

Of course the key is provided by Google itself after configuring the website in a personal account.

RecommendedAlternative
Google AnalyticsGoogle for it

Presentation

Those who make use of collections different from the posts default might find an enhanced version of the jekyll-paginate gem, called jekyll-paginate-v2.

This last isn’t compatible with Github pages, but in the case of a self-host solution, it provides the pagination support for:

  • all kinds of collections created;
  • posts grouped for tags;
  • post grouped for category;
  • multilanguage websites are supported with the locale flag;
  • also custom paginations based on filters, for category and/or tags, are available.

This is one of those gems that I used since the start and it is still present in my setup, especially for handling categories, tags, and collections it is pretty mandatory to have for me.

Another feature that every blog should provide is the Atom/RSS feed, here the choice is between the jekyll-feed plugin, or a simple layout handmade as I did.

I don’t remember well why I created my custom template, but jekyll-feed contains all the relevant options:

  • embeds the author data as specified in the authors.yml file;
  • provides a Liquid tag to include the HTML meta tag in the HEAD section;
  • automatically include an XSLT file if present;
  • the feed is encoded in the ATOM format;
  • can generate a feed for each category;
  • can generate a feed for each collection and a selection of categories;
  • can generate a feed for each tag or a limited number of them.
RecommendedAlternative
jekyll-paginate-v2jekyll-paginate
jekyll-feedManually create a layout file

Assets management

Jekyll itself manages the assets in a basic way: everything under the assets folder will be copied under the _site subfolder with the following precisions:

  • images are copied as they are, doesn’t matter if they are used or not, within the output folder;
  • SASS files will be converted to CSS and compressed, if that option is configured, also variables in the front matter is allowed. Be sure to include at least an empty front matter to trigger the conversion procedure;
  • to convert the CoffeeScript files to Javascript must install the jekyll-coffeescript gem.

Besides that there is the jekyll-assets gem, which allows to:

  • include the assets simply using the asset tag;
  • check for the files required and raise an error in case they don’t exist;
  • operate some changes to the images (resizing, rotation, cropping);
  • cache the assets using the Sprockets gem;
  • it supports several formats: CSS, SASS, SCSS, JS, ES6, CoffeeScript, SVG.

One of the features I needed more from it was the image transformation, the resizing mostly, to provide different sizes for at least the most relevant type of screen sizes (mobile, tablet, laptop/desktop screen).

As many did during the years, I also inspected the blog access statistics and realized that a small percentage of the traffic was coming from a mobile source.

On the other hand, creating different sizes for each image had these cons:

  • The space required was growing too much;
  • consequently, also the number of files to upload every time was growing;
  • the quality of resized images wasn’t as good as expected;
  • also the building time was affected.

Last but not least the caching mechanism that should have helped to reduce the building times wasn’t working so good: randomly I was getting all the cache invalidated and all the images rebuilt again although they where not changing.

That was affecting:

  • the final size, duplicating images already created that were not removed from the _site/assets folder;
  • the number of files to upload;
  • the time wasted to clean it all;
  • my mood!

Indeed to fix the problem I had to:

  1. Erase the cache;
  2. Erase the remote files;
  3. Recompile the site;
  4. Upload everything again because the image filenames, depending on the signatures, changed, and so their img tags refer to them.

After a while of struggling with that, I decided to step over, and first I got rid of the image resizing: a max resolution of 1024 x 768 with a good jpeg compression is more than enough, at this point the default Jekyll’s cache was more than good to handle that.

In brief, I removed jekyll-assets from my pipeline:

  • The assets are now directly served without intermediate addon by Jekyll;
  • to reduce the image size I convert them in webm format keeping the max resolution within the limits, cropping them to show only the portion of interest where needed;
  • for Javascript compression, I adopted the jekyll-minifier plugin.

After that, I experienced:

  • Reduction of the building time from 30+ to 5/6 seconds, and in general around 10 seconds (it depends mostly on whether or not the SASS files must be recompiled);
  • no more risks to re-upload the same files again and again, because the filenames never change;
  • reduced drastically the maintaining times;
  • no more stress.

The single feature I missed was the file check while embedding the images within my articles, also because Javascript and CSS files are recalled once within the layout.

To address that problem and the gallery creation I decided to build my custom jekyll-gallery-plugin, which allows to arrange the gallery data in the front matter, providing the basic info for each image, the filename, a title, and a description, and using a Liquid tag to render it within the content.

It also supports layout customization and, last but not least, checks for the validity of the filenames included in the gallery, raising a blocking error in the case it is not where supposed to be.

The gallery itself is built around the Glightbox library, not all options are supported, and not all options are supported already, but by overriding the layout files everything can be achieved.

RecommendedAlternative
Jekyll builtinjekyll-assets

Optimizations and automatizations

To optimize a Jekyll website there are several areas of intervention, starting from the aforementioned images and CSS question.

As I told I prefer to convert the images in a more performant format like webp, to do that I rely on the ImageMagick application which service I recall from a simple KDE Service Menu item in my Dolphin file manager.

However, also a Rakefile task might do the trick:

require "fileutils"

desc "Generate webp files"
task :webp do
  Dir.glob(File.join([ Dir.pwd, "assets/#{ENV['CONV_FOLDER']}/_source/*" ])) do |src|
    dest = File.split(src).delete_if{ |x| x == "_source" }
    dest = File.join(dest).gsub(/.*$/, ".webp")
    dest_dir = File.dirname(dest)

    FileUtils.mkdir_p(dest_dir) unless Dir.exist? dest_dir

    exec_in_folder("convert '#{src}' '#{dest}' ")
  end
end

The task takes charge of the subfolder specified in the CONV_FOLDER environment variable, looking for the files in the _source sub-subfolder and moving them, once converted, to the CONV_FOLDER:

$ CONV_FOLDER='my_subfolder' rake webp

To make it work correctly, be sure that there is only a single _source folder in the path: Jekyll automatically excludes from being processed the files and folders whose name starts with an underscore _.

If using the Jekyll builtin function, SCSS files are automatically compressed using this configuration in the _config.yml file:

sass:
  style: compressed

To optimize the HTML files there are at least two valid options:

The former plugin is a Liquid layout that must be copied under the _layouts folder and must be recalled by the default.html template to have all the other depending layouts compressed.

All the options are related to the HTML format; by default it removes closing tags, optional tags, and comments, making it not XHTML valid. It also works on GitHub pages, being a pure Liquid template.

The second plugin has more options:

  • can compress HTML, Javascript, JSON, and CSS files;
  • can remove the http(s) and javascript protocols;
  • can remove link, script and style attributes;
  • allows to use the uglifier tool with Javascript files.

What I found is that the first alternative is faster for HTML files, so I kept jekyll-compress for the HTML files and, configuring the right exclusions, used jekyll-minifier for the Javascript files only.

jekyll-minifier:
  # Exclude files from processing - file name, glob pattern, or array of file names
  # and glob patterns and toggle features and settings using:
  exclude: 
  - '*.xml'
  - '*.html'
  - '*.css'
  - '.htaccess'
  - '*.map'
  - '*.xslt'
  ...
  uglifier_args:
    harmony: true

Just remember to set up the uglifier option to don’t raise weird errors.

RecommendedAlternative
Jekyll builtin for CSS filesjekyll-minifier
jekyll-compress-htmljekyll-minifier
Rake file task for imagesjekyll-assets
jekyll-minifier for JS filesLeave them uncompressed

Tests

At the end of all we want to know whether or not all our jobs contain subtle errors, those kinds of errors difficult to spot, like wrong link references, missing tag properties, …

A tool that’s present as the NodeJS package is html-validate, it helps to check from the command line whether or not a particular HTML page respects the standards.

$ npm install -g html-validate

Several presets can be configured in the configuration file ~/.htmlvalidate.json, also more than one:

  • recommended;
  • standard;
  • prettier;
  • a11y;
  • document.
{
  "extends": ["html-validate:document"]
}

The recommended preset is the default and it is also very strict, while document is more relaxed. For a better understanding of the differences the preset table comes in help.

Using one of the HTML compressors implies the creation of web pages not exactly aligned with the stricter requests, that’s why, in that case, it’s better to assign one of the latest presets.

The set of rules applied are listed using the option --print-config over a file:

$ html-validate --print-config index.html

And finally, to examine the files:

$ html-validate _site

Another interesting tool that helps to check the validity of the HTML output is html-proofer.

This command line app belongs to the Ruby environment and can be installed as a Ruby gem:

$ gem install html-proofer

It is also intended to be integrated within other projects as a regular gem, but what is interesting to mention is the command-line application that can be used:

  • To check for the internal link validity:
    $ htmlproofer --disable-external=true --checks Links _site/
    
  • Or all the links provided:
    $ htmlproofer --checks Links _site/
    
  • The images:
    $ htmlproofer --checks Images _site/
    
  • The Javascript scripts:
    $ htmlproofer --disable-external=true --checks Scripts _site/
    
  • Or all together:
    $ htmlproofer --checks Links,Images,Scripts _site/
    
RecommendedAlternative
html-validate-
html-proofer-

Final thoughts

In this article, I covered the practices I followed to build the present blog until now, mentioned the plugins available on the internet and those published in my repository proposed my choices and also the alternatives.

Of course, all of that might change in the future: my trend is to rely on few and well-supported gems and create my own for the rest.

Indeed, besides all the stuff I mentioned, there are other small plugins that I have put directly into the _plugins folder to achieve different tasks:

  • Create custom Liquid tags;
  • handle CDN URL’s assets;
  • inject a permalink;
  • generate privacy policy pages;
  • provide the items for the related articles block.

The fact that Jekyll is built on Ruby makes things easier about everything related to the development and maintenance of whatever project, and as I told you in the first article it is a big pro for me. Someone might argue that there are faster executing solutions, but honestly must wonder what is better: a slower executed language, which is not so terribly slow, or a faster executed language that terribly slows down the development with its syntax and caveats?


Creative Commons License This work is licensed under a Creative Commons Attribution-NoCommercial-ShareAlike 4.0 International License


Leave a Comment