
A DIY HTML engine
The Good Doctor
Website development and hosting have come of age. Gone are the days of creating websites and blogs from scratch. Although blogging platforms and content management systems have many advantages, they still leave a lot to be desired. The benefits of do-it-yourself (DIY) website development still outweigh a packaged solution. Certain advantages are increased speed, lower or no hosting costs, security, and – more importantly – control over the flow of information and leaks that could happen by using rickety plugins or third-party infrastructure.
Jekyll started its journey in 2008, when a GitHub cofounder released the first build with an aim to provide a modern take on DIY website creation. A simple, static text-to-HTML site generator, Jekyll [1] provides a robust engine to render simple text into attractive web pages, in addition to providing enough leverage for power users to tinker with the code.
The Jekyll advantage originates in its low overhead. Its Liquid renderer and Markdown support allow users to create websites using simple text and scripts to render a fully blown web page. The engine's sophisticated design forms the backbone of GitHub Pages [2], so GitHub hosts Jekyll user websites for free.
Simplicity comes at a cost, however. Jekyll has made certain trade-offs to achieve an efficient design. The basic design flaw for website aficionados is the static design. Jekyll cannot render dynamic objects and does not allow the incorporation of a database, both of which tax resources and subsequently increase the cost of hosting a website.
In this article, I unravel the many aspects of Jekyll, from installing, understanding commands, configuring files, creating a project structure, managing the website, transferring it to your GitHub Pages user site, and tinkering with modular code.
Getting Started
Jekyll is written in the Ruby On Rails server-side web application framework; thus, your distro will not offer Jekyll packages directly. Instead, you should install the latest ruby and ruby-dev packages from your package manager. Jekyll usually requires the latest Ruby version. If your distro doesn't offer the latest version, simply build it from the Ruby website [3].
Once the latest Ruby build is installed, use the following command to install Jekyll:
gem install jekyll
Now you can initiate the jekyll
command directly from the terminal. Before I jump into the nitty-gritty of the setup process, though, I'll take a look at the major feature set successive versions have brought to the table.
History
Jekyll started its journey with the first public release of version 0.1 (Table 1). At that time, Jekyll lacked the charm of later iterations. Although major features were added in later point releases, the shot in the arm came with version 1. Developers abridged various features under simple commands to bring about a change in the overall user experience.
Tabelle 1: Version Features
0.1-1.0 |
1.1-2.0 |
2.1-3.x |
---|---|---|
-First public release |
- Jekyll doctor debugger |
- Kramdown replaces Maruku |
- Markdown support |
- Jekyll docs integration |
- Liquid profiler |
- Maruku implementation |
- Variables/modular code support |
- Code of conduct |
- Plugin system |
- Collections |
- Speed enhancements |
- |
- Sass support |
- Incremental regeneration |
- |
- CoffeeScript support |
|
- HTML 5 template |
|
|
Jekyll v1 also saw inclusion of the new
command, which generates a working skeleton of the website. Various compile tools were merged under build
, serve
, and migrate
. A draft folder was added for convenience to manage and transfer drafts on the fly.
Version 2 further bolstered stability with added support for Sass, a CSS extension language, and CoffeeScript, a compiler that produces JavaScript. The introduction of collections paved the way for a unique variety of article types, in addition to generic blog posts and pages. The Maruku Markdown interpreter was replaced by the kramdown Ruby-based Markdown converter.
Version 3 was more of an incremental, community-centric update that fixed many bugs and provided a much-needed grooming of the code. Several optimizations were made to enhance the user experience, speed, and resource utilization. The cherry on top was the implementation of incremental regeneration, which allowed Jekyll to update a website automatically without having to rebuild the entire project. Any changes or additions to files (barring _config.yml
) now are automatically updated to the local server. With a simple refresh of the web page, changes are visible on the fly. This nifty addition saves countless CPU cycles that would be needed to rebuild a project, even for a tiny change.
Jekyll releases are disruptive in nature, which can break your existing project. The rolling release keeps dropping support for older Ruby versions, which makes the whole process uncertain. Additionally, constant syntax changes and a new scripting engine keeps you on the edge to learn and implement cutting edge features.
Creating a Project
Once you are done with the installation, it's time to immerse yourself in the Jekyll magic. The jekyll new
command creates a rough sketch for your project. For this article, I created a project named linuxmagazine [4] with
jekyll new linuxmagazine
which tells Jekyll to create a new project directory within the working directory and ensures a working website, even without manual changes. Thus, the moment you create a project with Jekyll you have a working website at your disposal.
Changing to the newly created project directory and running
jekyll serve
cross-checks all the files and plugins and renders the website to a local server. The site is usually hosted under localhost:4000, to which you can navigate in your browser to see the basic site layout.
The default theme provides a minimalist design (Figure 1) and is frugal on resources. For this article, I use the default theme and build on it by leveraging various plugins and variables. Note that the new
and serve
commands require bundler
to manage dependencies, so during the build process, you might notice some exception messages in the terminal (Figure 2).


Like jekyll
, bundler
is a Ruby gem that has to be installed using Ruby commands, rather than a distro's package manager. To install, simply initiate
ruby install bundler
then close the existing running Jekyll server by pressing Ctrl+C and build the project again with
bundle exec jekyll serve
to render the website, perform version control, check and meet all dependencies, and host your project on a local server.
Project Structure
Since version 1, jekyll new
populates and creates a project template, making the development process simple. Understanding the project layout is imperative for the lucid functioning of your project. Your directory layout might differ if you use a different theme, but for homogeneous behavior, Jekyll implemented a code of conduct from version 2 onward for a streamlined directory layout.
The directory structure has been encapsulated, and certain directories are now stored directly under a theme configuration folder, which, however, can be overridden by simply creating a similar directory layout within the project folder. Jekyll will automatically append newly defined folders and files during build time.
Because I am using the minima theme, I will restrict my focus on its directory structure to show how the core directories and configuration file are organized (Figure 3).

The _config.yml
file manages, alters, and defines site parameters. Jekyll initially reads this file and then initiates the process of building the website.
Basic tasks, like setting up the website name, email, and descriptions, are defined within this configuration file. Because this file is not regenerated automatically, to reflect any changes, the project has to be rebuilt again.
The _config.yml
file is a one-stop configuration file for managing Jekyll; from defining the name of your website to setting the server or restricting directory inclusion, this file has it all. A unique feature lets you define a global variable here that can be called in any file within the purview of the project definition.
Simply define anything (e.g., name: linuxmagazine
), and you can use the variable (name
in this example) without having to redefine it. The syntax for reusing a variable is {{site:<variable>}}
. Note that site
here is not the name of your site but simply the word "site." For this example, I use {{site:name}}
to render the value defined in the variable name (i.e., linuxmagazine).
Another useful prebuilt variable is port:
, which lets you change the default port at which your local server is hosted. For example, by default, the port is set to 4000, but you can define any port number at which to host your local server, other than those pre-reserved for common file tasks (e.g., port:7000
will host the site at localhost:7000/).
Other important variables are exclude
, include
, and timezone
, to name a few. The include
variable forcefully includes any file that follows, and you can include dot (.) files, which are excluded by default in the build process. The exclude
variable will leave out a set of files and directories defined by the user during the build process.
Gemfile and Gemfile.lock
A core file is the bundler gem, a handy configuration file with which you define gems (and their versions) that you want to install and use with your project. Bundler manages and installs gems on the fly.
Bundler automatically generates Gemfile.lock
, which records the exact version of the gems installed. Then, when the same project is run on a different machine, bundler will read the Gemfile.lock
file to ascertain dependencies, thus avoiding the logjam that can occur when an updated version of the required gem exists. This comes in handy if you have not specified version control in the resultant Gemfile
.
Both Gemfile
and Gemfile.lock
are automatically generated, even if you don't initiate bundler. As mentioned previously, the jekyll new
command initiates bundle exec
by default.
The _posts
directory contains all your site content and normal blog posts: Content and layout can vary.
The _site
directory holds the generated assets. Once Jekyll transforms the rich text into HTML, it places all the rendered objects into this folder, which is not visible unless you render your project.
The very important _layouts
directory holds the layouts of the posts and pages you want to create. You can define a topic-specific layout and use it under the front matter, and you can create a layout that can exclude certain features of the website (e.g., posts that allow comments and others that don't).
The layout is initiated within the front matter of the post with the use of the layout
variable. You can define multiple layouts and save them under the _layouts
directory, substantially reducing duplication of code and helping to keep the structure spick and span.
The _includes
directory holds the modular code for your projects: All the scripts, modular code, and various site-specific files land here. This folder is a shot in the arm for streamlining your code. Files under _includes
are called by Liquid syntax (e.g., {% include <file>.html %}
), allowing you to call any file in a post that is predefined within the _includes
directory. You can also add certain conditions for the file to be included and pass a parameter to the included file when you initiate it (more about this later).
Modularity
Modular code forms the backbone and is intrinsic to Jekyll. Modularity enhances code readability, impedes code duplication, regulates resource overhead, and improves project maintainability. To cater to these principles of modularity, Jekyll has implemented various subtleties that aid in better management of projects and keep the clutter to a minimum. The major tools provided by Jekyll are:
- variables
- front matter
- Liquid
include
- layout
As previously mentioned, you can define variables in the _config.yml
file and make use of it to reduce repetition of data. However the scope and scale of these variables change with its definition. Two types of variables can be defined within a project: local and global. Global variables are defined in the config.yml
file and can be accessed by any file within the purview of the project. Local variables are specific to a post and are defined within a file. The usage of that variable is limited to that particular file.
Any variable defined in config.yml
has to be accessed through the modifier site
. To access a variable, you use {{site.<variable_name>}}
. The variable has to be used inside Liquid syntax ({{ }}
), or the program will render the expression in plain text.
Local variables are defined inside the front matter and can be accessed within that file. In this case, the syntax would be {{page.<variable_name>}}
. Variables can hold multiple values as integers, strings, and arrays.
The heart of the Jekyll rendering engine is the front matter, which is powered by YAML, which provides a very powerful yet simple way to manage and define the structure of a post.
The beginning of any Jekyll post starts and ends with ---
, which tells the renderer that text within these delimiters is YAML. Jekyll then treats and renders the projects, providing values to these variables to be used within the file.
No predefined structure defines a variable, and any variable can be defined as one deems fit. The flexibility to define, pass, and call a variable is immense and provides a user with ample room in which to work. Variables can have an array and objects to diversify the definition. To define a subset value, you use the format shown in Listing 1 within the front matter.
Listing 1: Front Matter
--- categories: tech magazine: - month: January cost: $15 - month: February cost: $10 - month: March cost: $12 comments: true ---
As you can see, a variable named magazine
has objects with associated values. You can use these objects within the Liquid tag and fetch data as needed.
Liquid programming syntax was developed to allow users to implement conditional statements and loops. With the use of Liquid, you can increase the productivity of code when coupled with front matter.
Liquid can be used in one of two ways: to call a variable defined within front matter or as a conditional statement with a modifier:
- Variable call. To call a predefined variable, you use curly brackets
{{ }}
containing objects such assite
orpage
and its variable to be called. - Conditional statement. A conditional statement uses curly brackets followed by an ampersand (
{% <condition> %}
) and ends with whatever condition is being used. In the example in Listing 1, then, you can render the output as:
<ul> {% for value in page.magazine %} <li> item.month, item.cost </li> {%endfor%} </ul>
In the for
loop, variables and objects extract values stored in the predefined variable magazine
. Similarly, you can use if
statements to create a conditional loop.
Because Jekyll doesn't support a database, if you want to set up a comment section in your blog posts, you have to resort to third-party Disqus comments. Disqus is a fairly popular and widely used blog comment hosting service that allows users to post a comment using their social profiles.
An admin uses include
and front matter to control where readers may post a comment. First, you should create a file within the _includes
directory that will act as a global file for Disqus (Listing 2). Save the code as comments.html
under the _includes
directory. From this point on you can use {{ include comments.html }}
where you want readers to comment. After that, register your website with Disqus and generate a short name for your website. Simply save that name in _config.yml
as <disqus-shortname>: linuxmagazine
.
Listing 2: Disqus Comment
01 {% if page.comments %} 02 03 <div id="disqus_thread"></div> 04 <script> 05 var disqus_config = function () { 06 this.page.url = PAGE_URL; // Replace PAGE_URL with your page's canonical URL variable 07 this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable 08 }; 09 (function() { // DON'T EDIT BELOW THIS LINE 10 var d = document, s = d.createElement('script'); 11 s.src = 'https://EXAMPLE.disqus.com/embed.js'; 12 s.setAttribute('data-timestamp', +new Date()); 13 (d.head || d.body).appendChild(s); 14 })(); 15 </script> 16 <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus</a>.</noscript> 17 18 {% endif %}
Notice the {% if page.comments %}
conditional statement at the start of the file, which checks for the variable comments
within the front matter of a post. If the value is true, it will load Disqus comments within that blog post. In the previous example, the front matter variable comments
holds a true value, so if you were to include this Disqus file in the above code, it would load the comment section for that blog post.
Reducing clutter by including a file whenever needed is a nice idea, but it becomes cumbersome and archaic, and you might even forget to include a file at some point. To avoid such situations, you can write a generic layout file that houses all the repetitive and necessary code needed for blog posts or any kind of site content.
Building the Blog
Now that you have a taste of Jekyll, it's time to tackle some advanced concepts that will help you engineer your site for a smooth experience. In this section, I show you how to add a dynamic contact form, site map, and subscribe form.
Remember that Jekyll is a static page generator, which means that activities such as subscription forms, contact forms, and Twitter and Facebook page widgets are not rendered within a page. However, you can source those tasks to third-party plugins or apps and subsequently convert a monologue into a conversation.
To implement a contact form for this article, I use Formspree [5], which provides a free API to implement a contact form (Listing 3). You can change your contact information, and any communication made through this section will be sent to an email address of your choice. Simply add the code to a new contact.html
file, and under front matter use permalink: <contact>
to create a new permanent link section at the top of the page.
Listing 3: Contact Form
<form action="https://formspree.io/abc@gmail.com" method="POST"> <input type="text" name="name"> <input type="email" name="_replyto"> <input type="submit" value="Send"> </form>
A site map comprises all the URLs associated with a website listed in a single file, which is the feeding ground for search engine crawlers. If you want your blog posts to be listed by the different search engines, a site map plays a significant role in validating your presence on the world wide web.
To add a site map, you have to add the jekyll-sitemap
gem to Gemfile
so that bundle
can automatically fetch and install it; then, simply add the gem to _config.yml
:
plugins: - jekyll-sitemap
Proceed to compile the website again with
bundle exec jekyll serve
and you should be able to access the site map at localhost:8000/sitemap.xml.
Subscription systems have become a mainstay on blogs and websites to remind readers of regular updates and provide a site owner with a wider reach and constant appeal. To create this system, I used MailChimp, a nifty subscribing system that allows you to get up to 2,000 subscribers for free – a wholesome number for a small website. As your audience size increases, you can always opt for the paid version.
Simply create a MailChimp account, and when you sign in, choose Lists from the menubar to create your subscriber list. Once you have created a new list, proceed to generate the code by selecting Subscriber Form under your newly created list and generating the style of code you want. Finally, save the code in a new newsletter.html
file under the _includes
directory. You can then use this code anywhere you like (Figure 4).

Hosting
Jekyll is compatible with GitHub Pages, so it is just a matter of adding a new repository and committing the project. To begin, you create a GitHub repository (e.g., linuxmagazine in this example) and use the command line to add all the project files. Assuming you have preconfigured Git, the process should be routine: Simply change to your project file directory and issue the following commands:
git add . git commit -m 'first commit' git remote add origin https://github.com/shashpant/linuxmagazine.git git push origin master
Be sure to replace the repository link with your own, and then open your repository on GitHub and navigate to the settings. Next, scroll down to the GitHub Pages static site hosting service (Figure 5), select the master branch, and click Save. GitHub will process your website, and within minutes, your site should be up and running.

Conclusion
The efficacy of Jekyll is in the same league as costly counterparts, paving the way for inexpensive website creation. The Jekyll Liquid implementation and the flexibility of front matter and Liquid syntax evokes object-oriented programming concepts, providing the potential for clean coding. The combination of HTML, JavaScript, and CSS allows you to render typical website components. A plethora of third-party add-ons, themes, and handy code makes Jekyll a thriving platform for content creation.
However, Jekyll's rolling release feature can be a weakness. The destructive way in which updates are handled can be cumbersome and can even break your website.
Overall, I am pleased with the modularity and stability of Jekyll. The icing on the cake is the GitHub support and free hosting. If you are looking to make a website, Jekyll is a viable option.