#if(x) {# compiles to if (x) {. There is no second language to learn, no directives to memorise and no template dialect that behaves almost like JavaScript.
The same template renders the first response on a server and every update in the browser. One implementation, not two that have to be kept in step with each other.
A template compiles once into an ordinary function, which the engine then optimises like any other. No virtual DOM, no diffing pass, no build step.
<div id="app">
<h1>#title#</h1>
<ul>
#for(let item of items) {#
<li>#item#</li>
#}#
</ul>
</div>
<script>
const data = {
title: 'Reading list',
items: ['Dune', 'Solaris', 'Ubik']
};
const view = new hashJS("app");
view.bind(data);
</script> const hashJS = require('./hashJS.js');
// Compile once at startup.
const list = hashJS.compile(`
<h1>#title#</h1>
<ul>
#for(let item of items) {#
<li>#htmlEncode(item)#</li>
#}#
</ul>`);
// Render per request.
app.get('/books', async (req, res) => {
res.end(list({
title: 'Reading list',
items: await db.books.titles()
}));
}); Nothing about the template changes when it moves between the two. See it running →
<script src="https://cdn.jsdelivr.net/gh/richdafunk/hashJS@v1.3.5/hashJS.js"></script>
require on a server: it exports through module.exports as well as to the global scope. .js file. #htmlEncode(value)# so it lands as text rather than as markup. new Function, so a page served under a Content Security Policy needs script-src 'unsafe-eval'.