Skip to main content

Vento

This is a minimal template engine inspired by other great engines like Nunjucks, Liquid, Mustache, and EJS.

Why another template engine?

Because I couldnā€™t find the ā€œperfectā€ template engine for me (probably this one neither is). The issues I found in existing template engines:

Nunjucks

(Itā€™s my favorite template engine so far).

  • I like:
    • I can invoke functions like {{ user.getName() }}.
    • Very flexible, with many built-in filters and features
  • I donā€™t like:
    • Itā€™s not well maintained. The last version was released in Jun 2022. And the previous version in 2020.
    • Itā€™s not async-friendly. For example, you have some tags to work with sync values (like for and if) and others for async values (like asyncEach and ifAysnc). Some features donā€™t work in async contexts.
    • To me, itā€™s very uncomfortable to have to type the delimiters {% and %} all the time (especially the % character).
    • By default, all variables are escaped, so you have to remember to use the safe filter everywhere. This is not very convenient for my use case (static site generators), where I can control all the content and the HTML generated.
    • Some filters are too specific.

Liquid

  • I like:

    • The support for async evaluation is less hacky than Nunjucks.
    • The variables are not escaped by default, thereā€™s an escape filter for that.
  • I donā€™t like:

    • Itā€™s not possible to invoke functions in a liquid template. For example {{ user.getName() }} fails.
    • It has the same problem as Nunjucks with the % character in the delimiters.

EJS/Eta

  • I like:
    • It allows running any javascript code in the template.
  • I donā€™t like:
    • It has the same problem with the % character. And I donā€™t like the opening and closing delimiters (<% and %>).
    • Because it runs javascript, itā€™s very verbose to do a simple forEach or if.

Mustache

  • I like:
    • Very simple, everything is inside {{ and }}.
    • The closing tag is {{/tagname}}, very nice!
  • I donā€™t like:
    • Perhaps too simple and the syntax can be a bit confusing.
    • Partials. Itā€™s not easy to include them dynamically.
    • The data context is a bit confusing to me.
    • Very uncomfortable to work with filters.

What this new template engine has to offer?

First, letā€™s take a look at this syntax example:

{{ if printName }}
  {{ await user.getName("full") |> toUpperCase }}
{{ /if }}
  • Everything is between {{ and }} tags. Unlike Nunjucks or Liquid, thereā€™s no distinction between tags {% tag %} and printing variables {{ var }}.
  • The closed tag is done by prepending the / character (like Mustache).
  • Async friendly.
  • Like EJS, you can use real JavaScript code everywhere. await user.getName("full") is real JS code that will be executed at runtime.
  • Filters are applied using the pipeline operator (|>). Note: this is not exactly like the last proposal for JavaScript, itā€™s inspired by (the previous proposal that was rejected but itā€™s way more simple and fits better for filters.
  • Filters can run prototype methods. In this example users.getName("full") returns a string, so the toUpperCase is a method of the String object. Itā€™s the same as users.getName("full").toUpperCase().

Getting started

See Getting started on the docs.

Editor support

See Editor integrations on the docs.