Simple, efficient and elegant dev.

The superpowers of template languages - Part 2

Last edition :

Table of contains

This article is the second in a series dedicated to server-side templating languages. In the previous article, we covered the goals of templates and an overview of the main templating languages. Now, let’s dive into the technical aspects and explore two major features of templates:

Injecting data into pages

Templating languages provide instructions to easily integrate data from the application into HTML pages. A template file is thus a mix of classic HTML/CSS and specific templating syntax elements, such as variables, loops, conditions, etc.

Example: A template file can be used to build a blog's article list from article metadata (title, date, tags, etc.).
The PHP, Python, JavaScript, or other backend code provides the collection of metadata, and the template iterates over this collection to construct an HTML list (using <ul> and <li> elements).

Here’s an excerpt from the first version of the Nunjucks template I used to build my blog’s article list. It uses a collection named category.posts containing the articles of a category.


{# List of posts of the category #}  
<ul id="postList">  
   {%- for post in category.posts %}  
   <li class="post" data-rank="0">      
      <a class="post-title" href="{{ post.url }}">{{ post.data.title }}</a>  
      <p>{{ post.data.description }}</p>  
      <div class="post-metadata">  
         <div class="post-taglist">Tags:   
            {%- for tag in post.data.tags %}<div class="post-tag">{{ tag }}</div>{%- endfor %}  
         </div>  
         <div>{{ labels.lastEdition }}:   
            <time class="post-date" datetime="{{ post.date | htmlDateString }}">{{ post.date | readableDate() }}</time>  
         </div>  
      </div>      
   </li>  
   {%- endfor %}  
</ul>

All the code between curly braces is Nunjucks template syntax. Three types of braces are possible:

In the above excerpt, you can see a few syntax elements:

Note

The | (pipe) operator allows calling a method while passing the starting object as a parameter.
Multiple | can be chained to call different methods in sequence. For example, the expression {{ object | filter1() | filter2() }} calls filter1() with object as a parameter, then filter2() with the result of filter1() as a parameter. This is called function composition, a powerful feature of some templating languages.

Without delving deeper into Nunjucks syntax, this example already gives you a good idea of how templating syntax integrates data into HTML. Now, let’s explore layout and composition techniques.

Managing Page Layouts with Template Inheritance

When creating templates for different page types (e.g., homepage, article list page, single article page, etc.), it’s common for these templates to share common parts like headers and footers.

Instead of repeating the code for these shared parts in every template, we can share them between templates using template inheritance. This concept is similar to what you’d find in object-oriented programming languages.

Warning

Advanced templating languages like Jinja2, Nunjucks, Twig, Django, Pug, and Blade support inheritance, but simpler languages like Handlebars, Mustache, HAML, or Markdown do not, nor does the Liquid language.

How template inheritance works

The inheritance relationship involves creating a base template containing all the shared elements and derived templates representing the different page types you need. Derived templates inherit all the content from the base template and can add or override specific elements (e.g., the main content area of the page).

For example, my Eleventy-powered blog uses 5 Nunjucks templates corresponding to different page types, all derived from a base template containing the header (with title and main menu) and footer.

Template Inheritance
The green shapes represent category and article pages. Articles are Markdown files, but they can still inherit from the Nunjucks layout post.njk thanks to an abstraction layer managed by Eleventy, which relies on frontmatter placed at the beginning of files (more info on this documentation page). However, in the following code examples, to avoid confusion, I’ll show you pure Nunjucks inheritance syntax.

Note

The inheritance relationship can, of course, be extended to more than two levels to create a more complex template hierarchy.

Implementing the inheritance relationship

As you’ll see, the technique is very simple.

Here’s an example structure for the base template:


<head>  
<!-- head content -->  
</head>  
<body>  
   <header>  
   <!-- header content -->  
   </header>  
   <main>  
      {% block mainContent %}{% endblock %}  
   </main>  
   <footer>  
   <!-- footer content -->  
   </footer>  
</body>

{% block mainContent %}{% endblock %} is Nunjucks syntax for defining a named block called mainContent, which is empty here but can be overridden in derived templates.

In all derived templates, add the following code to implement inheritance:


{% extends "base.njk" %}  

{% block mainContent %}  
<!-- code that describes the main content of the page -->  
{% endblock %}

The first line establishes the inheritance relationship. The following lines override the mainContent block by adding code specific to the derived template.

To be continued

Template inheritance already allows for efficient project structuring. But we can go further with advanced composition and modularity techniques, which we’ll explore in the final part of this series.

In the meantime, if you found this article helpful, please like it to help spread the word. 😉