Simple, efficient and elegant dev.

The Superpowers of Templates Languages - Part 3

Last edition :

Table of contains

This article is the final installment in a series dedicated to server-side templating languages.

The previous article demonstrated how to inject data into HTML pages and how to efficiently manage page layouts using template inheritance.

Now, let’s explore another fascinating topic for modern websites: how to create reusable components using templates.

The goal is to maintain a clean and maintainable server-side architecture, even when using a non-component-oriented server framework. As you’ll see, templating languages excel at meeting this challenge!

The Challenge

Template inheritance is an effective way to share code between templates when all derived templates need the entirety of the parent template’s code. In the previous article, we saw the classic example of a parent template containing a header and footer shared across all site pages.

But let’s take the example of a blog where multiple authors publish articles. Suppose we want to display a brief author bio at the bottom of each article and create a page listing all the blog’s authors.

templates composition

Creating a common ancestor for article and author list templates wouldn’t solve the need to display multiple authors on the same page.

Here, we clearly need an independent template to model an author, which can then be integrated into both article and author list templates. This calls for a composition relationship.

Tip

Template composition is well-suited for the following cases:

  • For template fragments repeated multiple times on the same page.
  • For template fragments present in several (but not all) page types.
  • To replace inheritance in templating languages that don’t support it.

There are two types of templates for composition:

Partial Templates

A partial template is simply a piece of a template stored in an independent file. Here’s an example of the partial template author.njk representing an author:

<div class="author">  
   <img src="/public/img/" alt="" class="author-photo"/>  
   <div>  
      <p class="author-name"></p>  
      <p class="author-bio"></p>  
   </div>  
</div>  

To include this partial template in the article template, add the following code where desired (e.g., at the beginning or end):


{% set author=metadata.authors | getAuthorById(authorId) %}  
{% include "components/author.njk" %}

The first line retrieves the author by filtering the metadata.authors collection using the authorId property of the article. The set instruction stores the author in the author variable, which is the one used in the author.njk component.

Important

The key point here is that the author template has access to the article template’s context, including the author. Unlike a component, a partial template doesn’t have its own data context but shares that of its host.

When generating HTML pages from the templates, each article page will include the code produced by the partial template.

Warning

Partial templates are a simple way to centralize static content. However, their lack of parameterization and shared context with the host significantly limits their usefulness. This creates a tight coupling between partial templates and their hosts, which undermines modular and maintainable long-term architecture.

Macros (= Components)

To avoid the coupling issue between partial templates and their hosts, some modern templating languages offer macros, which are very similar to the component concept found in frameworks like Nuxt.js.

Example

Let’s revisit our partial template author.njk and transform it into a macro:


{%- macro Render(author) %}  

<div class="author">  
   <img src="/public/img/{{ author.photo }}" alt="{{ author.name }}" class="author-photo"/>  
   <div>  
      <p class="author-name">{{ author.name }}</p>  
      <p class="author-bio">{{ author.bio }}</p>  
   </div>  
</div>  

{% endmacro %}

A macro is equivalent to a function and can accept parameters. Here, the Render macro receives the author object as a parameter.

To use this macro in the article template:


{% set author=metadata.authors | getAuthorById(authorId) %}  
{% import "../components/author.njk" as Author %}  
{{ Author.Render(author) }}

Important

The key takeaway: The macro doesn’t share the host template’s context but receives the data it needs via parameters. This eliminates any coupling between the two.

Note: The component’s CSS can be placed in an author.css file and imported into the base template using the standard syntax:

{% include "../components/author.css" %}

This way, the component’s CSS will be shared across all its instances if the component is used in multiple places.

Advantages of Macros Over Partial Templates

Macros:

Additionally, like partial templates, macros can be nested—meaning they can call one another.

Thus, even with server frameworks lacking native component support—such as Flask (Python), Symfony (PHP), Jekyll (Ruby), Express.js, and Eleventy (Node.js-based)—it’s possible to create clean, modular code using macros.

Note

Macros are particularly well-suited for static server-side rendering. They don’t replace the client-side component concept introduced by React.

Which Templating Languages Support Components?

Most advanced templating languages support the concept of components, whether they’re called components, macros, mixins, or functions, as shown in the table below:

Templating
Language
Framework Component
Implementation
Example Syntax
Nunjucks Node.js macros {% macro %}/{{ macro() }}
Jinja2 Flask, Django macros Same as Nunjucks
Twig Symfony macros Same as Nunjucks
Blade Laravel components <x-component>
Pug Node.js mixins mixin/+mixin()
EJS Express.js JS functions <% function() { %>

Conclusion

Mastering a powerful templating language is a major asset for creating clean, modular, and maintainable code. In particular, components enable modular code architecture even with server frameworks that don’t natively support the concept.

For my static blog, I use the Nunjucks templating language with the Eleventy framework. I discovered both at the same time, and it took me a few days to get comfortable with Nunjucks’ syntax. Now, I truly appreciate its power and expressiveness.

I use template inheritance for layouts and components for breadcrumbs and article summaries (on category pages). My code architecture remains simple and clean, and I can handle complex challenges like translation and taxonomy much more easily, with greater customizability and performance than with WordPress. It’s a real pleasure!

Finally, I’d love to hear which templating languages you use and whether you’re satisfied with them. Share your thoughts in the comments! And if you found this article helpful, please like it to help spread the word. 😉