Template engines help us put dynamic data into our Html pages. At runtime, the template engine replaces variables in a template file with actual values and transforms the template into an HTML file sent to the client.

In this blog, I will walk you through 3 fast template engine with examples. I have considered a simple user profile Express.js application with a form to accept name and age also an index/listing page which shows the detail of all users.



1. Squirrelly.js

Squirrelly is a modern, configurable, and blazing fast template engine implemented in JavaScript. It works out of the box with ExpressJS and the full version weighs only ~2.2KB gzipped.

It is not white-space sensitive.

Installation

npm install squirrelly --save

After it is required in the index.js file, make sure the view engine is specified as squirrelly.

// index.js

//...
app.set('view engine', 'squirrelly');
app.set('views', 'views');
//...

One more thing to note is that all files in which squirrelly are used must be saved with a .squirrelly extension and located in the views folder in the root directory of the project.

app.js
package.json
package-log.json
views
    |_ new.squirrelly
       index.squirrelly

Usage

Form

As shown below a form has two fields that accept name and age.

There are two global references used in the form pageTitle and formButton. It is identified as a global reference because data value is put between the opening and closing delimiters.

These global references value will be replaced by the value sent from users.js file. Here, {{pageTitle}} gets replaced by New User and {{formButton}} gets replaced by Save.

<!-- /views/new.squirrelly -->

<html>
<head>
    <title>{{pageTitle}}</title>
</head>
<body> 
    <main>
        <form action="/users/" method="POST">
            <div>
                <label for="name">Name</label>
                <input type="text" name="name" id="name">
            </div>
            <div>
                <label for="age">Age</label>
                <input type="text" name="age" id="age">
            </div>
            <button class="btn" type="submit">{{formButton}}</button>
        </form>
    </main>
</body>
</html>

In the routes/users.js file, I have used a render method which is provided by express.js which accepts the default template engine as the first parameter and the second parameter will be the data that can be used in the view.

When described as new in the render method, it will start looking for new.squirrelly file in the views folder since we have set the view engine as squirrelly in the index.js file.

Also, pageTitle and formButton becomes the global reference which are passed into the template.


// routes/users.js

//...
router.get('/new', (req, res) => {
    res.render('new', { pageTitle: 'New User', formButton: 'Save' });
})
//...


Index

In the index page, I am listing all the user details. For that, I have looped over user objects by using foreach where options.users give all the users that are sent from users.js file.

Inside the loop, you can get a single user object data by calling @this and if you want to display name then you can call @this.name

<!-- /views/index.squirrelly -->

<html>
<head>
    <title>{{pageTitle}}</title>
</head>
<body>
    <ul>
        {{foreach(options.users)}}
            <li>
                Name: {{@this.name}}
                Age: {{@this.age}}
            </li>
        
    </ul>
</body>
</html>

I have set up my application by using mongoose hence I call find() on the User model to fetch all the user objects and I pass this in the render method with key as users which is used in the view file above.


// routes/users.js

//...
router.get('/', (req, res) => {
  let users = User.find()
                    .then((users) => {
                      res.render('index', { pageTitle: 'Users', users: users });
                    })
                    .catch((error) => {
                      res.json({ success: false, error: error })
                    })
})
//...

You can learn about this template engine here



2. Marko.js

Marko is a fast and lightweight HTML-based templating engine that compiles templates to CommonJS modules and supports streaming, async rendering, and custom tags. It is HTML re-imagined as a language for building dynamic and reactive user interfaces.

Installation

npm install marko

To load Marco templates, we have to say to node.js module loader about how to load and compile Marco templates. We can achieve that by adding the below line of code in the index.js file

//index.js
//...
require("marko/node-require").install();
// ...

All files should be saved with an extension .marko.

Usage

Form

Using the same example, I have a form with two fields name and age.

With Marko, you can have dynamic data in the template by using a dollar sign with opening and closing delimiters.

pageTitle and formButton should be called on data or input to get replaced with the value defined in the users.js file. Here, ${data.pageTitle} or ${input.pageTitle} gets replaced by New User and ${data.formButton} or ${input.formButton} gets replaced by Save.

<!-- /views/new.marko -->

<html>
<head>
    <title>${data.pageTitle}</title>
</head>
<body> 
    <main>
        <form action="/users/" method="POST">
            <div>
                <label for="name">Name</label>
                <input type="text" name="name" id="name">
            </div>
            <div>
                <label for="age">Age</label>
                <input type="text" name="age" id="age">
            </div>
            <button class="btn" type="submit">${data.formButton}</button>
        </form>
    </main>
</body>
</html>

In the routes/users.js file, we have to require the Marko template just like any other javascript module.

We should call the render method on a template by passing data to be used in template as the first argument and the second argument will be the output stream that you want to write to.


// routes/users.js

var template = require('../views/new.marko');

//...
router.get('/new', (req, res)=>{
  template.render({ pageTitle: 'New User', formButton: 'Save' }, res);
})
//...

When we see the editor after the page is reloaded, we see a new file new.marko.js that Marko has generated which contains the compiled javascript code for the template.

Image1


Index

In the index page, I will be listing all the user details. For that, I have looped over user objects by using for attribute that is processed by Marko template compiler.

Inside the loop, you can get a single user object data by calling user and if you want to display name then you can call user.name

<!-- /views/index.marko -->

<html>
<head>
    <title>${data.pageTitle}</title>
</head>
<body>
    <ul>
        <for|user| of=data.users>
            <li>
                Name: ${user.name}
                Age: ${user.age}
            </li>
        </for>
    </ul>
</body>
</html>

In the routes/users.js file, I again define which template to use for listing page by requiring the marko template. And also render method on the template variable just like I have used for new page.


// routes/users.js

var template = require('../views/index.marko');

//...
router.get('/', (req, res) => {
  let users = User.find()
                    .then((users) => {
                      template.render({ pageTitle: 'Users', users: users }, res);
                    })
                    .catch((error) => {
                      res.json({ success: false, error: error })
                    })
})
})
//...

When we see the editor after the page is reloaded, we see a new file index.marko.js that Marko has generated which contains the compiled javascript code for our template.

Image1

You can learn about this template engine here



3. Swig.js

Swig is a Fast, Django-like template engine. It automatically escapes all output for safe HTML rendering.

Installation

npm install swig
npm install consolidate --save

After swig is installed, install consolidate. Consolidate.js is a template engine consolidation library that will help us integrate swig templates into our application.

By using app.engine we are telling our application to map the swig template engine to .html files. And also setting the default view engine to .html.

// index.js

var cons  = require('consolidate');

//...
app.engine('html', cons.swig);
app.set('view engine', 'html');
app.set('views', 'views');
//...

Store all files with .html extension under views folder in the root directory of the project.

Usage

Form

I have used the same form with two fields name and age.

The syntax of using template variables is similar to squirrelly. We can use swig template variables within the double curly braces. pageTitle and formButton are two template placeholders which will get replaced by the dynamic values.

<!-- /views/new.html -->

<html>
<head>
    <title>{{pageTitle}}</title>
</head>
<body> 
    <main>
        <form action="/users/" method="POST">
            <div>
                <label for="name">Name</label>
                <input type="text" name="name" id="name">
            </div>
            <div>
                <label for="age">Age</label>
                <input type="text" name="age" id="age">
            </div>
            <button class="btn" type="submit">{{formButton}}</button>
        </form>
    </main>
</body>
</html>

In the routes/users.js file, I have used a render method which is provided by express.js which accepts template engine as the first parameter and second parameter will be the data that can be used in the view.


// routes/users.js

//...
router.get('/new', (req, res)=>{
  res.render('new', { pageTitle: 'New User', formButton: 'Save' });
})
//...


Index

In the index page, I am listing all the user details - name and age. Below is the example of how to loop the users object in swig.js.

Inside the loop, you can get a single user object data by calling user and if you want to display name then you can call user.name

<!-- /views/index.html -->

<html>
<head>
    <title>{{pageTitle}}</title>
</head>
<body>
    <ul>
        {% for user in users %}
            <li>
                Name: {{user.name}}
                Age: {{user.age}}
            </li>
        {% endfor %}
    </ul>
</body>
</html>

In users.js file, I have specified index as the file to render on success and I am passing the object with pageTitle and users as keys.


// routes/users.js

//...
router.get('/', (req, res) => {
  let users = User.find()
                    .then((users) => {
                      res.render('index', { pageTitle: 'Users', users: users });
                    })
                    .catch((error) => {
                      res.json({ success: false, error: error })
                    })
})
//...

I hope this blog gave you an idea of how to use Squirrelly, Marko and Swig in your application.

Keep Coding !!!

Contact us to work on your website.

- Ameena