Skip to content
Web's smartest templating engine.
JavaScript HTML
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
bench MOD update readme Sep 18, 2019


Web's smartest templating engine. Super-lightweight, outstanding performance, no dependencies.

Getting Started  •  Options  •  API  •  Mikado CLI  •  Custom Builds  •  Template Compiler  •  Template Server  •  Express Middleware (SSR)

This library was build by reversed engineering with these primary goals as its base:

  1. providing a clean, simple and non-cryptic tool for developers who focus on living standards and common styles
  2. designer-readable templates based on pure html (most famous and compatible markup in the web)
  3. providing the best overall performance


  • Mikado Runtime (Render Templates)
    npm install mikado

  • Mikado Compiler (Compile Templates)
    npm install mikado-compile

  • Mikado Server (Serve Templates)
    npm install mikado-server

  • Express Middleware (Server-Side Rendering) *WIP*
    npm install mikado-express



  1. Basic Example (Classic Javascript)
  2. Basic Example (ES6 Modules)
  3. TodoMVC App: Source Code / Run Demo
  4. js-framework-benchmark

Take a look into the source code of this pages is the best starting point.

Work in progress:

  • Conditional branches
  • Includes/partials
  • Live templates (for local development)
  • Persistent state
  • Express Middleware
  • Loops (through partials)
  • Plugin API

Get Latest:

Build File CDN
mikado.min.js Download
mikado.light.js Download
mikado.custom.js Custom Build

To get a specific version just replace /master/ with one of the version numbers from the release e.g. /0.0.8/, or also a commit hash.


npm install mikado

Feature Comparison

Feature mikado.min.js mikado.light.js
Template Engine
VDOM Cache
Event Binding -
Data Binding -
Persistent Storage -
Transport/Load Templates -
Export/Import Views -
VDOM Manipulation Helpers -
Conditional Branches -
Includes/Partials -
File Size (gzip) 4.1 kb 1.6 kb

Benchmark (Stress Test)

Run the benchmark:

Sources and readme:

Values represents operations per second, each benchmark task has to process an data array of 100 items. Higher values are better except file size (minified, gzip) and memory (allocation during the whole test).

Rank Library Size kB Memory B Create Update Partial Append Repaint Reduce Remove Score
1 mikado-0.1.2 2 167336 1599 7158 12829 838 251559 28938 25969 945
2 domc-0.0.12 4.46 207488 957 6575 11284 865 92368 20701 24041 704
3 sinuous-0.14.2 7.48 265192 384 9695 11996 384 308152 17639 13464 626
4 redom-3.24.1 2.88 246936 369 2632 4790 381 58468 19843 11991 446
5 inferno-7.3.1 8.4 410960 706 2623 3199 617 5538 6527 14009 344
7 innerHTML - 406992 983 956 854 425 847 1742 26346 343
6 surplus-0.5.3 15.79 166288 698 632 640 282 639 1460 15469 294
8 jquery-3.4.1 31.26 819032 746 650 578 305 565 895 4919 158

Score = Sumtest(self_ops / max_ops) / test_count * 1000

The maximum possible score is 1000, that requires a library to be best in each category.
Read more about this test here.

API Overview


Global methods:

Global methods (not included in mikado.light.js):

Instance methods:

Instance methods (not included in mikado.light.js):

Cache helpers (not included in mikado.light.js):

Instance properties:


Each Mikado instance can have its own options.

Option Description Default
root The destination root where the template should be rendered. null
template The template which should be assigned to the Mikado instance.
async Perform render tasks asynchronously and return a Promise. false
cache Enable/disable caching. Caching can greatly increase performance (up to 20x). Be careful, it fully depends on your application, there are situations where a enabled cache performs slower.
Recommendation: enable caching when several of your item data will stay unchanged from one to another render task. Disable caching when changes on data requires a fully re-render more often.
store Passed items for rendering are also stored and synchronized along the virtual dom. You can re-render the full state at any time, without passing the item data.
Notice: When passing an external reference of an existing Array-like object to the field "store" the store will perform all modifications directly to this reference (read more about "Extern Storage").
loose When storage is enabled this flag removes also item data whenever a corresponding dom element was removed. false
reuse When enabled all dom elements which are already rendered will be re-used for the next render task. This performs better, but it may produce issues when manual dom manipulations was made which are not fully covered by the template. Whe enabled make sure to use the Virtual DOM Manipulation helpers. true
state Pass an extern object which should be referenced as the state used within template expressions. { }
once Performs the render of a template just one time. This only applies on static views. The render ist starting immediately (do not call .render again!). When finishing it fully cleans up (removes view, item data and also the template definition). This is useful for static views, which should persist in the app. false

Basic Example

Install Mikado via NPM or include one of the distributed builds:

npm install mikado

To make the command line interface available you have to install via NPM.

Define a HTML-like template and use double curly brackets to mark dynamic expressions which should be calculated during runtime:


Save this template e.g. to template/template.html.

The preserved keyword item is a reference to a passed item. You can access the whole nested object.

Mikado comes up with a template compiler which has to be run through Node.js and provides a command line interface (CLI) to start compilation tasks. The template compiles into a fully compatible JSON format and could also be used for server-side rendering.

Install Mikado Compiler via NPM:

npm install mikado-compile

Compile the template through the command line by:

mikado compile template/template.html

Notation: mikado {{input}} {{destination}}

Instead of mikado compile you can also use npx mikado compile alternatively. When a destination was not set, the input folder will be used instead.

After compilation you will have 4 different files:

  1. template.js the template compiled in ES5 compatible Javascript
  2. template.es6.js the template compiled as an ES6 module
  3. template.json the template compiled in JSON-compatible notation (to load via http request)
  4. template.html the HTML-like template (reference, do not delete it)

Assume there is an array of data to render (or just one item):

var items = [{
    user: "User A",
    tweets: ["foo", "bar", "foobar"]
    user: "User B",
    tweets: ["foo", "bar", "foobar"]
    user: "User C",
    tweets: ["foo", "bar", "foobar"]

Load library and initialize template (ES5):

<script src="mikado.min.js"></script>
<script src="template/template.js"></script>
    var view ="template");

The name of a template inherits from its corresponding filename.

Load library and initialize template (ES6):

<script type="module">
    import Mikado from "./mikado.js";
    import template from "./template/template.es6.js";
    var view =;

After creation you need mount to a DOM element initially as a destination root and render the template with data:


You can also chain methods:

var view =;

Rules and Conventions

Every template has to provide one single root as the outer bound. This is a convention based on the concept of Mikado.

Instead of doing this in a template:


Provide one single root by doing this:


You can also use a <div> or any other element as a template root.

Advanced Example

A bit more complex template:

<section id="{{}}" class="{{this.state.theme}}" data-index="{{index}}">
    {{@var is_today = ===}}
    <div class="{{item.class}} {{is_today ? 'on' : 'off'}}">
        <div class="title" style="font-size: 2em">{{item.title.toUpperCase()}}</div>
        <div class="content {{index % 2 ? 'odd' : 'even'}}">{{#item.content}}</div>
        <div class="footer">{{view.parseFooter(item)}}</div>

You can use any Javascript within the {{ ... }} curly bracket notation.

To pass html markup as a string, the curly brackets needs to be followed by a "#" e.g. {{#...}}

To use Javascript outside an elements content you need to prevent concatenation of the returned value. For this purpose the curly brackets needs to be followed by a "@" e.g. {{@...}}

Within a template you have access to the following indentifiers:

Identifier Description Passed Mode
item A full reference to a passed data item. auto
view An optional payload used to manually pass in non-item specific data or helper functions. manual
index Represents the index of the currently rendered item. auto
this Provides you access to the Mikado view instance. auto
this.state An object used to keep data as a state across runtime. State data are shared across all Mikado instances. auto (manual set) Gives access to the internal item store (Array). auto
this.length The length of all items actually rendered (to get length of stored items use instead). auto
window The global namespace auto
self Points to the current rendered element itself. Using "js" node property or by using the {{@ marker grants you to have "self" available. auto

You cannot change the naming of those preserved keywords.

It is recommended to pass custom functions via the view object (see example above). You can also nest more complex computations inline as an IIFE and return the result.

<div class="date">{{ 
        var date = new Date();
        // ...
        return date.toLocaleString(); 

Alternatively of accessing item, view, index and this.state you can also access variables from the global namespace.

For performance relevant tasks avoid passing html contents as string.

To finish the example above provide one single or an array of item data:

var items = [{
    id: "230BA161-675A-2288-3B15-C343DB3A1DFC",
    date: "2019-01-11",
    class: "yellow, green",
    title: "Sed congue, egestas lacinia.",
    content: "<p>Vivamus non lorem <b>vitae</b> odio sagittis amet ante.</p>",
    footer: "Pellentesque tincidunt tempus vehicula."

Provide view payload (non-item specific data and helper methods used by the template):

var payload = {
    page: 1,
    today: "2019-01-11",
    parseFooter: function(item){ return item.footer; }

Provide state data (application specific data and helper methods used by the template):

view.state.theme = "custom";

Create a new view instance or initialize a new template factory to an existing instance:


Mount to a new target or just render the already mounted template:

view.render(items, payload);

Render asynchronously by providing a callback function:

view.render(items, payload, function(){

To render asynchronously by using promises you need to create the view instance with the async option flag:

view =, { async: true });

view.render(items, payload).then(function(){

Event Bindings

Lets take this example:

<table data-id="{{}}" root>
        <td click="show-user">{{item.user}}</td>
        <td><a click="delete-user:root">Delete</a></td>

There are 2 click listeners. The attribute value represents the name of the route. The second listener has a route separated by ":", this will delegate the event from the route "delete-user" to the closest element which contains the attribute "root".

Define routes:

view.route("show-user", function(node){


}).route("delete-user", function(node){


Routes are stored globally, so you can share routes through all Mikado instances.

List of all native supported events:

  • change, input, select, toggle
  • click, dblclick
  • keydown, keyup, keypress
  • mousedown, mouseenter, mouseleave, mousemove, mouseout, mouseover, mouseup, mousewheel
  • touchstart, touchmove, touchend
  • submit, reset
  • focus, blur
  • load, error
  • resize
  • scroll

Synthetic events:

Event Description
tap The tap event is a synthetic click event for touch-enabled devices. It also fully prevents the 300ms click delay. The tap event automatically falls back to a native click listener when running on non-touchable device.
swipe * This gesture is currently in progress.

Create, Initialize, Destroy Views

Create view from a template with options:

var view =, options);

Create view from a template with options and also mount it to a target element:

var view =, template, options);

Mount a view to a target element:


Initialize an existing view with new options:


Initialize an existing view also with a new template:

view.init(template, options);

Unload/delete the template which is assigned to a view:


Destroy a view:


Render Templates (Assign Data)

When using storage, every render task also updates the storage data.

Render a template incrementally through a set of item data:


Render a template with item data and also use view-specific data/handlers:

view.render(items, payload);

Schedule a render task asynchronously to the next animation frame:

view.render(items, payload, true);

Schedule a render task by using a callback:

view.render(items, payload, function(){
    // finished

Schedule a render task by using promises (requires option async to be enabled during initialization):

view.render(items, payload).then(function(){
    // finished

Render a static template (which uses no dynamic content):


Repaint the current state of a dynamic template (which has item data, requires store to be enabled):


Repaint the current state on a specific index:


Just create a template without adding/assigning/rendering to the root:

var partial = view.create(item);

Modify Views

Add one item to the end:


Add one item before an index:

view.add(items, 0); // add to beginning

Append multiple items to the end:


Append multiple items before an index:

view.append(items, 0); // append to beginning

Remove a specific item/node:


Remove first 20 items:


Remove last 20 items:


Remove next 20 items of a given node (including):

view.remove(node, 20);

Remove previous 20 items of a given node (including):

view.remove(node, -20);

Remove all:


Replace an item/node:

view.replace(old, new);

Update an single item/node:

view.update(node, item);

Re-Sync DOM:


Useful Helpers

Get a template root node from the DOM by index:

var node = view.node(index);

Get an item from the store by index:

var item = view.item(index);

Get the index from a given node:

var index = view.index(node);

Find a node which corresponds to a data item (same reference):

var node = view.find(item);

Find the first node which corresponds to a data item which has same content (that requires each item to be unique, otherwise use where):

var node =;

Find all nodes which matches a given payload (will always return an array, empty if no results was found):

var node = view.where({
    title: "foo",
    active: true,
    content: "bar"
var node = view.where(item);

Get the length of all items rendered (in store):

var length = view.length;

Get the current template name which is assigned to a Mikado instance:

var name = view.template;

Manipulate Views

Manual changes on the DOM may require re-syncing. To prevent re-syncing Mikado provides you several helper functions to manipulate the dom and also keep them in sync. Using the helper function also grants you a maximum performance.

Move an item/node to a specific index:

view.move(node, 15);  // 15 from start
view.move(node, -15); // 15 from end

Move an item/node to the top or bottom:


Move an item/node by 1 index:


Shift an item/node by a specific offset:

view.up(node, 3);
view.down(node, 3);

Move an item/node before or after another item/node:

view.before(node_a, node_b);
view.after(node_a, node_b);

Swap two items/nodes:

view.swap(node_a, node_b);

Caching Helpers

Set attribute of a node (will not replaces old attributes):

view.setAttribute(node, "href", "/foo");
view.setAttribute(node, {
    id: "foo",
    href: "/foo"

Get attribute of a node:

var attr = view.getAttribute(node, "href");

Set class name of a node (fully replaces old classes):

view.setClass(node, "class_a class_b");
view.setClass(node, ["class_a", "class_b"]);

Get class names of a node (returns an array):

var classlist = view.getClass(node);

Set inline styles of a node (fully replaces old styles):

view.setCSS(node, "top: 0; padding-right: 10px");
view.setCSS(node, ["top: 0", "padding-right: 10px"]);

Get all inline styles of a node:

var css = view.getCSS(node);

Set inline styles of a node (will not replaces old styles):

view.setStyle(node, "padding-right", "10px");
view.setStyle(node, {"top": 0, "padding-right": "10px"});

Get a specific inline style of a node:

var style = view.getStyle(node, "padding-right");

Set text of a node:

view.setText(node, "This is a title.");

Get text of a node:

var text = view.getText(node);

Set inner html of a node:

view.setHTML(node, "<b>This is a title.</b>");

Get inner html of a node:

var html = view.getHTML(node);


Enable storage by passing the options during initialization:

var view = new Mikado(root, template, {
    store: true,
    loose: true

Whenever you call the .render() function with passed item data, this data will keep in cache. Mikado will handle those data for you.


You can re-render the last/current state at any time without passing items again:


Or force an update to a specific index:


Access to the store:

var store =;

Do not de-reference the store, e.g.:

var store =;
// ...
store = [];

Instead do this:

var store =;
// ... = store = [];

Export / Import Views

You can export the latest rendering state of a view along with its item data to the local storage.


You can import and render the stored view by:


When exporting/importing templates, the ID is used as key. The template ID corresponds to its filename.

You cannot export several instances of the same template which holds different data. Also the state is not included in the export.

Loose Option

When loose is enabled Mikado will use a data-to-dom binding strategy rather than keeping data separated from rendered elements/templates. This performs generally faster and has lower memory footprint but you will also loose any item data at the moment when the corresponding dom element was also removed from the screen (render stack). In most situation this shouldn't be an issue, but it depends on your application.

Extern/Custom Store

You can also pass an reference to an external store. This store must be an Array-like type.

var MyStore = [ /* Item Data */ ];

Pass in the external storage when initializing:

var view = new Mikado(root, template, {
    store: MyStore,
    loose: false,
    persist: false


State pretty much acts like passing a view payload when rendering templates. State also holds an object but instead used to keep data across runtime. State data are also shared across all Mikado instances. State is directly assigned to each Mikado instance and do not has to pass during rendering. This all differ from using view payloads.

Define state properties: = new Date(); = function(){ return === new Date() };

You can assign any value as state or function helpers. Do not de-reference the state from the Mikado instance. When using export() the state will just export non-object and non-functional values. You need to re-assign them when the application starts.

Using extern states:

var state = {
    date: new Date(),
    today: function(){ return === new Date() }

Assign extern states during initialization:

var view = new Mikado(root, template, {
    state: state

Transport / Load Templates

Mikado fully supports server-side rendering. The template (including dynamic expressions) will compile to plain compatible JSON.

If your application has a lot of views, you can save memory and performance when loading them at the moment a user has requested this view.

Templates are shared across several Mikado instances.

Load template asynchronously into the global cache:

Mikado.load("", function(error){

Load template asynchronously with Promises into the global cache:

Mikado.load("", true).then(function(){




Load template synchronously by explicit setting the callback to false:

Mikado.load("", false);

Assign template to a new Mikado instance, mount and render:

var view ="template");

.load() loads and initialize a new template to an existing Mikado instance:


.init() assigns a new template to an instance:


.mount() assigns a new root destination to an instance:


.unload() unloads a template by name (filename):


Chain methods:


Static Templates

When a template has no dynamic expressions (within curly brackets) which needs to be evaluated during runtime Mikado will handle those templates as static and skips the dynamic render part. Static views could be rendered without passing item data.

Once (One-time rendering)

When a template just needs to be rendered once you can create, mount, render, unload and destroy (full cleanup) as follows:
      .unload() // unload before destroy!

Destroy has a parameter flag to automatically unload before destroy:, template)

You can also simply use a shorthand function:

Mikado.once(root, template); // static view
Mikado.once(root, template, items); // dynamic view
Mikado.once(root, template, items, payload, callback);

When destroying a template, template definitions will still remain in the global cache. Maybe for later use or when another instances uses the same template (which is generally not recommended).

When unloading templates explicitly the template will also removes completely. The next time the same template is going to be re-used it has to be re-loaded and re-parsed again. In larger applications it might be useful to destroy also dynamic views when it was closed by the user to free memory.

Compiler Service / Live Templates

Mikado provides you a webserver to serve templates via a simple RESTful API. This allows you to send views live from a server. Also this can be used for live reloading templates in a local development environment.

Install Mikado Server via NPM:

npm install mikado-server

Start the compiler server:

mikado server

Instead of mikado server you can also use npx mikado server alternatively.

The service is listening on localhost. The API has this specification:



  • localhost:3000/json/template/app.html
  • localhost:3000/json/template/app (WIP)
  • localhost:3000/template/app.json (WIP)

They all have same semantics, you can use different forms for the same request.


json Assign them manually via Mikado.register or just render the template once.
es6 Import as an ES6 compatible module.
es5 Uses Mikado from the global namespace. This requires a non-ES6 build of mikado or import "browser.js", both before loading this template.
js A synonym for es5.

Local Development

The compiler service is also very useful to render templates ony the fly when modifying the source code. Use a flag to switch between development or production environment in your source code, e.g.:

// production:
import tpl_app from "./path/to/app.es6.js";
import tpl_view from "./path/to/view.es6.js";

    // development:
    Mikado.load("http://localhost:3000/json/path/to/app.html", false);
    Mikado.load("http://localhost:3000/json/path/to/view.html", false);
    const app ="app");
    const view ="view");
    const app =;
    const view =;

// same code follows here ...

You can also import them as ES6 modules directly via an asynchronous IIFE:

let tpl_app, tpl_view;

(async function(){
        // development:
        tpl_app = await import('http://localhost:3000/es6/test/app.es6.js');
        tpl_view = await import('http://localhost:3000/es6/test/view.es6.js');
        // production:
        tpl_app = await import("./path/to/app.html");
        tpl_view = await import("./path/to/view.html");

// same code follows here ...
const app =;
const view =;

Server-Side Rendering (SSR)

Use the json format to delegate view data from server to the client. Actually just static templates are supported. An express middleware is actually in progress to create templates with dynamic expressions. When using SSR it may be useless to keep the template data as well as calculate all the optimizations for re-using. For this purpose use the method Mikado.once().

Best Practices

A Mikado instance has a stronger relation to the template as to the root element. Please keep this example in mind:

This is good:

var view = new Mikado(template);


This is bad:


Instead doing this:

var view_a = new Mikado(tpl_a);
var view_b = new Mikado(tpl_b);
var view_c = new Mikado(tpl_c);


Ideally every template should have initialized by one (and only one) Mikado instance and should be re-mounted when using in another context. Re-mounting is very fast but re-assigning templates is not as fast.


Partials gets its own instance under the hood. This results in high performance and also makes template factories re-usable when sharing same partials across different views.

Be aware of circular includes. A partial cannot include itself (or later in its own chain). Especially when your include-chain growths remember this rule.

You can include partials as follows:

    <title include="title"></title>
    <article include="article" as="item.content"></article>
    <footer include="footer"></footer>

The include attribute is related to the template name (filename), the as attribute is the reference which should be passed as the item to the partial.

Please notice, that each template requires one single root. When the template "template/title" has multiple nodes in the outer bound then wrap this into a new element as root or include as follows:

    <include>{{ template/title }}</include>
    <include as="item.content">{{ template/article }}</include>
    <include>{{ template/footer }}</include>

Use pseudo-element:

    <include from="title"/>
    <include from="article" as="item.content"/>
    <include from="footer"/>

The pseudo-element <include> will extract into place. You cannot use dynamic expressions within curly brackets, just provide the name of the template.

In this example the template "template/title" gets the tag <title> as the template route.

Loop Partials

    <title>{{ item.title }}</title>
    <tweets include="tweet" for="" max="5"></tweets>

In this example the template "tweet" loops the render through an array of tweets. The template "tweet" will get the array value from the current index as item.

Inline Loops

    <title>{{ item.title }}</title>
    <tweets for="item.tweets">
        <section>{{ item.content }}</section>

Conditional Branches

<main if="item.tweet.length">
    <title>{{ item.title }}</title>
    <section>{{ item.content }}</section>
    <footer>{{ item.footer }}</footer>
<main if="item.contacts.length">
    <title>{{ item.title }}</title>
    <section>{{ item.content }}</section>
    <footer>{{ item.footer }}</footer>
    <title>{{ item.title }}</title>
    <tweets if="item.tweets.length" for="item.tweets">
        <section>{{ item.content }}</section>
    <title>{{ item.title }}</title>
    <tweets for="item.tweets">
        <section if="item.content">{{ item.content }}</section>

About Data Binding

Instead of bind nodes/vnodes to a data item Mikado bind/references data items to nodes. The loose option lets you control how is data handled when its corresponding node was removed from the dom. You might think there is a drawback in missing diffing, but lets take an example:


Assume that the items content stay unchanged and just the order should changes to:


Some libs may try to swap nodes to its new place. But that has a noticeable bigger impact to the dom of a factor of 5 and higher (!) in comparison when existing nodes wil just re-rendered with the new data (for which the option reuse is for). The decoupled event delegation of Mikado allows you to keep listeners referenced to the right node. Surely that's not the same, but I never had a situation where this was an issue. Just keep in mind to use template notations for any value which should change dynamically (e.g. the attribute "checked") and you are save. Since Mikado was reversed engineered and the goal "keeping real nodes" was not on the table, it shouldn't be a mandatory criteria for you. If so, then you are lucky that you can use one of the dozen other templating libs out there.

Custom Builds

Perform a full build:

npm run build

Perform a light build:

npm run build:light

Perform a custom Build:


On custom builds each build flag will be set to false by default.

The custom build will be saved to dist/mikado.custom.xxxxx.js (the "xxxxx" is a hash based on the used build flags).

The destination folder of the build is: /dist/

Supported Build Flags
Flag Values Info
DEBUG true, false Log debugging infos
SUPPORT_CACHE true, false VDOM Cache
SUPPORT_EVENTS true, false Template event bindings
SUPPORT_STORAGE true, false Template data binding
SUPPORT_HELPERS true, false VDOM Manipulation helpers
SUPPORT_ASYNC true, false Asynchronous rendering (Promise Support)
SUPPORT_TRANSPORT true, false Load templates through the network

Compiler Flags
USE_POLYFILL true, false Include Polyfills (based on Ecmascript 5)

Target language

Copyright 2019 Nextapps GmbH
Released under the Apache 2.0 License

You can’t perform that action at this time.