Comparing HTML Preprocessor Features | CSS-Tricks

Of the languages ​​that browsers speak, I would bet that the very first that developers decided that further processing was needed was HTML. Every single one CMS in the world (except deliberately only headless CMSs) is essentially an elaborate HTML processor: they take content and squoosh it along with HTML templates. There are dozens of other dedicated HTML processing languages ​​available today.

That most important HTML processing needs are:

  • Compose complete HTML documents from parts
  • Template HTML by injecting variable data

There are plenty of Other things features they can have and we’ll get to that, but I think they’s biggies.

This research comes to you with the support of Frontend Masters, CSS-Tricks’ official learning partner.

Do you need front-end development training?

Frontend Masters is the best place to get it. They have courses on all the major front-end technologies, from React to CSS, from Vue to D3 and on with Node.js and Full Stack.

Diagram showing parts and {{data}} turns into a complete HTML document.

Consider PHP. It’s literally a “Hypertext Preprocessor.” On this very site, I make use of PHP to put together snippets of template HTML to build the pages and complete content you are looking at now.

<h2>
  <?php the_title(); // Templating! ?>
</h2>

<?php include("metadata.php"); // Partials! ?>

In the code above, I have squooshed some content into an HTML template, which calls another PHP file that probably contains more template HTML. PHP covers the two biggies of HTML processing and is available with cost-effective hosting — I guess that’s a big reason why PHP-powered websites run a huge portion of the entire Internet.

But PHP is certainly not the only HTML preprocessor, and it requires a server to work. There are many others, some designed specifically to run during a construction process Before the site is ever in demand by the users.

Let’s go language by language and see if it supports certain features and how. Whenever possible, link the preprocessor name to relevant documents.

Does it allow templates?

Can you mix data into the final HTML output?

Processor Example
Pug
- var title = "On Dogs: Man's Best Friend";
- var author = "enlore";
h1= title
p Written with love by #{author}
IS B
<%= title %>
<%= description %>

<%= @logged_in_user.name %>

Markdown
PHP
<?php echo $post.title; ?>
<?php echo get_post_description($post.id) ?>
Also has HEREDOC syntax.
Slender
tr
td.name = item.name
Haml
<h1><%= post.title %></h1>
<div class="subhead"><%= post.subtitle %></div>
Liquid
Hello {{ user.name }}!
Go to html / template
{{ .Title }}
{{ $address }}
Styr
{{firstname}} {{lastname}}
Moustache
Hello {{ firstname }}!
Twig
{{ foo.bar }}
Nunjucks
<h1>{{ title }}</h1>
Set
<!-- $myVar = We finish each other's sandwiches. -->
<p> <!-- $myVar --> </p>
Sergey

Make it part / inclusive?

Can you compose HTML from smaller parts?

Processor Example
Pug
include includes/head.pug
IS B
<%= render 'includes/head' %>
Markdown
PHP
<?php include 'head.php'; ?>
<?php include_once 'meta.php'; ?>
Slender ⚠️
If you have access to the Ruby Code, it looks like it can do that, but you will need to enter custom helpers.
Haml
.content
=render 'meeting_info'
Liquid {% render head.html %}
{% render meta.liquid %}
Go to html / template {{ partial "includes/head.html" . }}
Styr ⚠️
Only by registering a part in advance.
Moustache {{> next_more}}
Twig {{ include('page.html', sandboxed = true) }}
Nunjucks {% include "missing.html" ignore missing %}
{% import "forms.html" as forms %}
{{ forms.label('Username') }}
Set <!-- @import "someFile.kit" -->
<!-- @import "file.html" -->
Sergey <sergey-import src="https://css-tricks.com/comparing-html-preprocessor-features/header" />

Does it include local variables with include?

As in, can you pass on data to included / partial so that they can be used specifically? In Liquid you can e.g. Pass another parameter of variables that the partial must use. But in PHP or Twig there is no such capability – they can only access global variables.

Processor Example
PHP
IS B <%= render(
partial: "card",
locals: {
title: "Title"
}
) %>
Markdown
Pug
Slender
Haml .content
= render :partial => 'meeting_info', :locals => { :info => @info }
Liquid {% render "name", my_variable: my_variable, my_other_variable: "oranges" %}
Go to html / template {{ partial "header/site-header.html" . }}
(The period at the end is “variable scope.”)
Styr {{> myPartial parameter=favoriteNumber }}
Moustache
Twig
Nunjucks {% macro field(name, value="", type="text") %}
<div class="field">
<input type="{{ type }}" name="{{ name }}" value="{{ value | escape }}" />
</div>
{% endmacro %}
Set
Sergey

Does it make loops?

Sometimes you just need 100 <div>s, you know? Or more likely, you need to loop over a series of data and output HTML for each entry. There are lots of different types of loops, but having at least one is nice and you can generally make it work for what you need to loop.

Processor Example
PHP for ($i = 1; $i <= 10; $i++) {
echo $i;
}
IS B <% for i in 0..9 do %>
<%= @users[i].name %>
<% end %>
Markdown
Pug for (var x = 1; x < 16; x++)
div= x
Slender - for i in (1..15)
div #{i}
Haml (1..16).each do |i|
%div #{i}
Liquid {% for i in (1..5) %}
{% endfor %}
Go to html / template {{ range $i, $sequence := (seq 5) }}
{{ $i }}: {{ $sequence }
{{ end }}
Styr {{#each myArray}}
<div class="row"></div>
{{/each}}
Moustache {{#myArray}}
{{name}}
{{/myArray}}
Twig {% for i in 0..10 %}
{{ i }}
{% endfor %}
Nunjucks {% set points = [0, 1, 2, 3, 4] %}
{% for x in points %}
Point: {{ x }}
{% endfor %}
Set
Sergey

Does it make sense?

Mustache is famous for being philosophically “logical”. So sometimes it is desirable to have a template language that does not interfere with any other functionality, forcing you to handle your business logic in a different layer. Sometimes a little logic is just what you need in a template. And in fact, even Mustache has some basic logic.

Processor Example
Pug #user
if user.description
h2.green Description
else if authorised
h2.blue Description
IS B <% if show %>
<% endif %>
Markdown
PHP <?php if (value > 10) { ?>
<?php } ?>
Slender - unless items.empty?If you enable logical minor mode:
- article
h1 = title
-! article
p Sorry, article not found
Haml if data == true
%p true
else
%p false
Liquid {% if user %}
Hello {{ user.name }}!
{% endif %}
Go to html / template {{ if isset .Params "title" }}
<h4>{{ index .Params "title" }}</h4>
{{ end }}
Styr {{#if author}}
{{firstName}} {{lastName}}
{{/if}}
Moustache
It’s a little ironic that Mustache calls themselves “Logical Templates,” but they do little have logic in the form of “inverted sections.”
{{#repo}}
{{name}}
{{/repo}}
{{^repo}}
No repos :(
{{/repo}}
Twig {% if online == false %}
Our website is in maintenance mode.
{% endif %}
Nunjucks {% if hungry %}
I am hungry
{% elif tired %}
I am tired
{% else %}
I am good!
{% endif %}
Set
It can emit a variable, if it exists, which it calls “optional”:
<dd class="<!-- $myVar? -->"> Page 1 </dd>
Sergey

Does it have filters?

What I mean by filter here is a way to output content but change it on the way out. Avoid e.g. Special characters or use large text.

Processor Example
Pug ⚠️
Pug thinks of filters as ways to use other languages ​​within Pug, and does not come with any out of the box.
IS B
Whatever Ruby has, like:
"hello James!".upcase #=> "HELLO JAMES!"
Markdown
PHP $str = "Mary Had A Little Lamb";
$str = strtoupper($str);
echo $str; // Prints MARY HAD A LITTLE LAMB
Slender ⚠️
Private only?
Haml ⚠️
Very specific for removing spaces. Mostly to integrate other languages?
Liquid
Many of them and you can use more.
{{ "adam!" | capitalize | prepend: "Hello " }}
Go to html / template ⚠️
Has a lot of features, many of which are filter-like.
Styr ⚠️
Triple parentheses escape HTML, but otherwise you have to register your own block helpers.
Moustache
Twig {% autoescape "html" %}
{{ var }}
{{ var|raw }} {# var won't be escaped #}
{{ var|escape }} {# var won't be doubled-escaped #}
{% endautoescape %}
Nunjucks {% filter replace("force", "forth") %}
may the force be with you
{% endfilter %}
Set
Sergey

Does it have math?

Sometimes math is baked right into the language. Some of these languages ​​are built on top of other languages ​​and thus use the other language to count. Just like Pug is written in JavaScript, so you can write JavaScript in Pug, which can do math.

Processor Support
PHP <?php echo 1 + 1; ?>
IS B <%= 1 + 1 %>
Markdown
Pug - const x = 1 + 1
p= x
Slender - x = 1 + 1
p= x
Haml %p= 1 + 1
Liquid {{ 1 | plus: 1 }}
Go to html / template
{{add 1 2}}
Styr
Moustache
Twig {{ 1 + 1 }}
Nunjucks {{ 1 + 1 }}
Set
Sergey

Does it have slots / blocks?

The concept of a castle is a template that has special areas in it that are filled with content if available. It looks conceptually partial, but almost in backwards. Just like you could think of a template with partials like the template that calls these partials to put together a page, and you almost think of slots as little data that calls a template to make itself a complete page. Vue is famous for having slots, a concept that found its way into web components.

Processor Example
PHP
IS B
Markdown
Pug
You can pull it out with “mixins”
Slender
Haml
Liquid
Go to html / template
Styr
Moustache
Twig {% block footer %}
© Copyright 2011 by you.
{% endblock %}
Nunjucks {% block item %}
The name of the item is: {{ item.name }}
{% endblock %}
Set
Sergey <sergey-slot />

Does it have a special HTML syntax?

HTML has and even though space means little (a space is a space, but 80 spaces is also … a space), it’s not really a space – dependent language like Pug or Python. Changing these things is a language choice. If all the language does is add extra syntax, but otherwise you write HTML as normal HTML, I consider that it is not a special syntax. If the language changes how you write normal HTML, it is a special syntax.

Processor Example
PHP
IS B In Ruby, if you will, you generally do Haml.
Markdown
This is pretty much the whole point of Markdown.
# Title
Paragraph with https://css-tricks.com/comparing-html-preprocessor-features/(#link).

- List
- List

> Quote

Pug
Slender
Haml
Liquid
Go to html / template
Styr
Moustache
Twig
Nunjucks
Set ⚠️
HTML Comment Directives.
Sergey ⚠️
Some invented HTML tags.

Wait wait – what about things like React and Vue?

I agree that these technologies are component-based and are used to create templates and often create complete pages. They can also do many / most of the functions listed here. Them and the many other JavaScript-based frameworks like them are also generally capable of running on a server or during a build step and producing HTML, although it sometimes feels like an afterthought (but not always). They also have other features that can be extremely compelling, such as scoped / encapsulated styles, which require collaboration between HTML and CSS, which is an enticing feature.

I did not include them because they are generally consciously used to essentially low DOM. They are focused on things like data retrieval and manipulation, governance, interactivity and such. They are not really focused on just being an HTML processor. If you use a JavaScript framework, you probably does not need a dedicated HTML processor, although it can definitely be done. For example, mixing Markdown and JSX or mixing Vue templates and Pug.

I did not even put native web components on the list here because they are very JavaScript focused.

Other considerations

  • VelocityHow quickly is it treated? Are you worried?
  • Language – What was in what is it written in? Is it compatible with the machines you need to support?
  • Server or build – Does it require a web server running to work? Or can it be run once during a construction process? Or both?

Superchart

Template Includes Local variables Loops Logic Filters Mathematics Slots Special syntax
PHP
IS B ⚠️
Markdown
Pug ⚠️
Slender ⚠️ ⚠️
Haml ⚠️
Liquid
Go to html / template ⚠️
Styr ⚠️
Moustache
Twig
Nunjucks
Set ⚠️
Sergey ⚠️
William

Leave a Reply

Your email address will not be published.