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:
- The Jekyll project at its 4th major release reached a certain stability and has been widely proven to be a good solution;
- It is a project used by GitHub itself to create the Github pages;
- 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;
- 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:
- 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;
- all the subfolders of _vendor won’t be copied under the _site/assets path because of its prefix;
- 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.
Recommended | Alternative |
---|---|
Custom theme | Pick one |
Yarn | Bundler |
Foundation | Bootstrap |
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.
Recommended | Alternative |
---|---|
Jekyll-compose | Manually create the files |
Jekyll-admin | Use 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:
- JSON-LD Site and post metadata for richer indexing
- Open Graph title, description, site title, and URL (for Facebook, LinkedIn, etc.)
- Twitter Summary Card metadata
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:
- a comments section provided by the Disqus service, I also published a plugin for that;
- a sitemap automatically generated by jekyll-sitemap plugin;
- some share-to-socials group of links, like my jekyll-share-plugin.
Recommended | Alternative |
---|---|
jekyll-seo-tag | Manually create an include file |
jekyll-disqus-plugin | Manually create an include file |
jekyll-sitemap | Manually create the layout |
jekyll-share-plugin | Manually 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.
Recommended | Alternative |
---|---|
Google Analytics | Google 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.
Recommended | Alternative |
---|---|
jekyll-paginate-v2 | jekyll-paginate |
jekyll-feed | Manually 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:
- Erase the cache;
- Erase the remote files;
- Recompile the site;
- 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.
Recommended | Alternative |
---|---|
Jekyll builtin | jekyll-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 jekyll-compress-html plugin;
- the jekyll-minifier plugin.
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.
Recommended | Alternative |
---|---|
Jekyll builtin for CSS files | jekyll-minifier |
jekyll-compress-html | jekyll-minifier |
Rake file task for images | jekyll-assets |
jekyll-minifier for JS files | Leave 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/
Recommended | Alternative |
---|---|
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?
This work is licensed under a Creative Commons Attribution-NoCommercial-ShareAlike 4.0 International License
This article refers to extra contents available as projects, further info is provided at the following pages:Jekyll-Disqus Jekyll-Share Jekyll-Gallery
Leave a Comment