Alternatives to vanilla HTML

Bret Cameron
Apr 11 · 11 min read

HTML is the first language that many developers learn. While learners of CSS and JavaScript soon encounter tools to augment these languages — such as Sass (for CSS) or Babel (for JavaScript) — the same is rarely true of HTML.

Since many HTML learners are total beginners, introducing new technologies would probably feel like too much too soon. But that means systems used to enhance and improve HTML get left off the curriculum, and many beginner-to-intermediate developers may not realise the options available to them.

So, in this article, we’ll explore four of the most popular templating engines for HTML. We’ll look into why you might choose to use them, and where you’re likely to encounter them in other tech-stacks. I’ll also provide sample CodePens for each one. By the end of the article, you should feel equipped with several options for writing faster, more concise HTML in your projects, whether your go-to programming language is JavaScript, Ruby or something else. Let’s dive in!

The problem with vanilla HTML

HTML stands for Hypertext Markup Language, and as a markup language, it does not have the functionality of a typical programming language. Markup languages are designed to be easy-to-read by humans as well as machines, and they typically use tags to define elements within a document.

But what makes HTML easy-to-read is also one of its limitations:

  • Any interactive elements, such as the onclick event, must be defined in the language in advance; in vanilla HTML, new events cannot be added by users.
  • It is not simple to add dynamic data into vanilla HTML.
  • If you want to generate repeated elements, there are no in-built shortcuts in the form of loops (for, while and so on). Nor is there an in-built method for handling conditionals (if … else or switch statements).
  • Vanilla HTML doesn’t allow users to define new, re-usable tag types.
  • Lastly, even though HTML is simple to read, it could be faster to write. Opening and closing tags can be a pain to type again and again, and parts of HTML may seem verbose, e.g. <button type="button" /> .

No templating system or HTML-like syntax solves all of these issues — in part because there’s no need for HTML to become a complete programming language. But these systems do intend to make writing HTML faster and more capable!

The alternatives to HTML templating systems

Before moving on to the four HTML templating systems, it’s worth mentioning a couple of alternatives ways to improve on vanilla HTML. Depending on what technologies you use, these may be all you need.

Emmet Abbreviations

These provide a much quicker way of writing vanilla HTML, and so it solves one of HTML’s main shortcomings: speed of writing. Emmet is available as a plugin for pretty much every major code editor — and as a bonus, it even offers snippets for CSS.

As an example of what Emmet can do for you, take this abbreviation: navigation#main>img.logo+ul#navigation>li*5>a{Link $} .

The abbreviation above produces the following HTML code, but it takes a fraction of the time to write:

<navigation id="main">
<img src="" alt="" class="logo"></img>
<ul id="navigation">
<li><a href="">Link 1</a></li>
<li><a href="">Link 2</a></li>
<li><a href="">Link 3</a></li>
<li><a href="">Link 4</a></li>
<li><a href="">Link 5</a></li>

Here, # denotes ID, . denotes class, + signifies a sibling, and > signifies a child. You can specify internal text with curly braces {}, use * to multiply elements, and use $ to number your repeated elements.

For a list of what Emmet abbreviations can do, check out the official cheat sheet. You can add Emmet abbreviations to your code editor-of-choice using one of the links below:

(Often, through the use of extensions or a bit of extra configuration, you can also use Emmet Abbreviations to write JSX. I explained how to do this with Visual Studio Code in a previous article.)

HTML in other languages

There are several technology-specific solutions to writing HTML. React.js has JSX, Ruby has eRuby (or ERB), and PHP has, well, “HTML in PHP”. For Java, there’s JSP (JavaServer Pages), while ASP.NET uses so-called Razor syntax to demarcate HTML code. The list goes on.

These solutions solve many of the limitations of vanilla HTML, mainly by allowing the possibility of loops, conditionals, and other programming-language features.

However, we won’t look into these solutions in detail because — unlike the remainder of technologies in this article — you don’t really have a choice whether or not to use these in your projects. You could use React without JSX, but that’s not what it’s built to do: you’d be missing out on a lot of great features, and probably causing yourself more trouble than if you just used JSX. As such, you’ll pick up these solutions as and when you choose to learn the technologies.

The remainder of this article focuses on templating systems, which mostly come into play in situations where you’d otherwise be using plain old vanilla HTML.

1. Pug (a.k.a Jade)

Underlying Language: JavaScript

Pug is the main focus of this article because it is built on top of JavaScript — the language of web development, and a popular choice here on Medium!

The markup used by Pug.js is much simpler than regular HTML, but it can take a little while to get used to. It, like most of the templating systems we’ll be discussing, achieves much of its simplicity by relying on line breaks and indentation.

So, whereas many languages allow you to get away with non-standard formatting and still work correctly, Pug does not. Changing the indentations will change the functionality of the code. Pug looks like this:

h1 The Title
h2 The Sub-title
p The main body.

This is equivalent to the following regular HTML:

<article id="main">
<h1>The Title</h1>
<h2>The Sub-title</h2>
<div class="article-body">
<p>The main body</p>

It’s clear from even a small example that Pug.js takes up less space and — without carets or closing tags — it is a lot quicker to write. The best place to learn about Pug is from its official docs, but here we’ll explore a couple of useful features.

Loops in Pug

Pug allows you to write inline JavaScript code, which is demarcated by using - , = and != — depending on whether you want to use buffered, unbuffered, or unescaped unbuffered JavaScript.

One of the most useful outcomes of this is the ability to write loops. So, for example, we can use a for loop to make a simple list of 3 items and number them accordingly:

- for (let x = 0; x < 3; x++)
li= 'Item ' + (x + 1)

This is equivalent to the following HTML.

<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>

Or, to iterate through the items in an array, we can use:

- var colors = ["Red", "Yellow", "Green", "Blue"]
each item in colors
li= item

This would be the same as writing:


Conditionals in Pug

Another useful feature is the ability to use conditionals within the markup code. We can render one of two alternatives using a simple boolean, such as:

- var isTrue = true
if isTrue
p Boolean is TRUE
p Boolean is FALSE

Or, for a more complex example, you can use case , which is shorthand for JavaScript’s switch statements:

- var raceResult = 7
case friends
when A
p You won the race!
when 1
p You were the runner up!
p You finished the race after #{raceResult - 1} other runners!

Pug.js ScratchPad

To try Pug.js yourself, you can use the following CodePen.

  • Above the horizontal line, you can see your rendered Pug.js.
  • Below the line, you can see the equivalent HTML markup.

Another way to test Pug is in your browser’s console. Type pug.render(p Hello World!) and press enter!


Underlying Language: Ruby

HAML stands for HTML Abstraction Markup Language. Like Pug, it uses indentations to define nested elements. It was developed as an alternative to Ruby’s ERB, but it can also be used in place of PHP, ASP, and — of course — vanilla HTML.

The main difference between HAML and Pug.js is that programming in HAML is done via Ruby. So, if you’re using JavaScript-based tech stack, Pug may be the safer choice.

In order to loop through items in HAML, we’d need to borrow from Ruby syntax, like so:

- (1..10).each do |i|
%div #{i}

This prints the numbers 1 to 10, each in a separate div.

Or, to demonstrate a simple conditional, we could use the following. Notice that all the logic is preceded by a dash - .

- isSunny = false
- if isSunny
%p Let's go outside!
- else
%p I'd rather stay inside.

HAML Scratchpad

This CodePen demonstrates the exact same capabilities as Pug’s above. Notice that comments in Pug become part of the rendered HTML while they do not in HAML.

3. Slim

Underlying Language: Ruby

Slim is another popular choice, and it has a lot in common with HAML: that’s because they’re both based around Ruby. Like HAML, it is possible to embed JavaScript in Slim, and the thorny question of whether to choose one over the other should probably be left as a topic of debate for the Ruby community!

On the surface, Slim seems to use slightly more minimalist syntax than HAML. Perhaps the most significant syntactic difference is that Slim uses | for each block of text, while HAML uses % before each tag.

/ Slim
tag(attr= “value”)
| text
%tag{attr: “value”}

Because the differences between Slim and HAML are minimal, you probably don’t need any further guidance before trying it out:

4. Markdown

Finally, we come to Markdown. This is not like the other systems discussed so far, as it doesn’t come with any programming-language capabilities, and you’ve almost certainly encountered it before. Most README files on GitHub are written using Markdown (using the .MD file extension ).

Though Markdown lacks programming features like loops and conditionals, it is superior to vanilla HTML by being both extremely simple to write and highly-readable by others — whether or not the code is actually rendered. It is also a lot easier to read than .txt , the file extension traditionally used for READMEs.

In Markdown h1 , h2 , and h3 are written as # , ## and ### . A single line of code is distinguished using ` while a block of code uses ``` , and links are written using a combination of square brackets and parentheses. For a fuller list of the shortcuts, check out the official cheat sheet.

Tables are probably the most fiddly part of Markdown, but if you search “generate markdown table”, you’ll find plenty of tools to make the process easier. To see examples of all of these features and more, check out the CodePen below.


I hope this article was useful, and that it has opened your eyes to the world of HTML templating.

The four templating systems chosen for this article are just a handful of what’s on offer. They were chosen partly for their popularity, and partly because you can easily try them out in CodePen.

I’d also like to give an honourable mention to Handlebars.js. It allows you to write code identical to vanilla HTML, except that you can insert JavaScript expressions using single, double or triple curly braces {} , making it pretty similar to JSX, but without requiring React.

If your favourite HTML templating tool is missing from this list, feel free to bring it up in the comments!

Bonus: Get Pug.js running on your local machine

CodePen makes it easy to try out the various templating systems described in this article, but what if you want to use them on your local machine? As an example, I’ll walk you through one way to test out Pug.js in a local project.

One of the places you’re most likely to encounter Pug is Express.js. So, to test it out, let’s create a simple Express project. If you’ve never done this before, I’ll provide all the necessary to code to set up a very simple server.

1. Ensure you have Node installed, then navigate to a folder of your choice in the terminal and type:

$ npm init

You can accept all the defaults except, for the sake of convention, let’s useserver.js — rather than index.js — as our main file.

2. Next, we’ll want to install a minimum of three dependencies: Express (to set up a simple server), Nodemon (to automatically refresh that server), and Pug. To do that, type the following in the terminal.

$ npm i -s express pug nodemon

3. Let’s create our server.js file. This article isn’t a back-end tutorial, so feel free to copy and paste the following code without changing anything. This will set up a simple server on port 1337 of your localhost.

// Import Express
const express = require('express'), app = express();
// Import Pug
const pug = require('pug');
// Choose a port
const port = 1337;
// Listen for connections on the specified port
app.listen(port, () => console.log(`Listening on port ${port}!`));
// Routes HTTP GET requests to the root path
app.get('/', (req, res) => res.send(pug.renderFile('template.pug', {
name: 'John Smith'

The key things to note are that we are importing pug using require() , and we’re then using the renderFile() method to display the contents of a file called template.pug . This method the only part of the file you might want to change, so you can add or remove dynamic data like name: 'John Smith' .

4. Now let’s create template.pug . This is your Pug sandbox to try whatever you like! If you need some inspiration, why not grab a block of code from the section on Pug, above? Try inserting #{name} to access your dynamic variable!

5. Before running your program, add the following to the scripts object in package.json , to allow the server to reload automatically whenever you make a change.

"scripts": {
"nodemon": "nodemon server.js"

5. Lastly, type nodemon into the terminal. You can now navigate to localhost:1337 on your web browser to see your Pug.js code being rendered!

Bret Cameron

Written by

Writer and developer based in London. On Medium, I write about JavaScript and web development 💻

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade