#hashJS

One template language, on the server and in the browser

«Plain and powerful JavaScript Syntax»

Get started Examples Github
A hashJS template beside the page it renders

Syntax you already know

#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.

Server and browser

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.

Nothing in the way

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.

11 kBone file, unminified
0dependencies
0build steps
MITlicence
In the browser
<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>
On a server — same template
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 →

🚀 Getting started

Drop in one file. There is nothing to install and nothing to configure:

The same file works under require on a server: it exports through module.exports as well as to the global scope.

🔒 Writing templates safely

A template is compiled and executed, so the template itself is code — treat it exactly as you would treat a .js file.
Never build a template out of user input.
Concatenating submitted text into the template string lets that text run as JavaScript. This is the one thing the library cannot protect you from.
Rendering user data is fine.
Data is substituted, never executed. Wrap anything that came from a user in #htmlEncode(value)# so it lands as text rather than as markup.

⚙️ Deploying under a Content Security Policy

Compiling a template turns it into a function, which means new Function — and a strict policy blocks that unless it allows script-src 'unsafe-eval'.

You do not have to grant it. hashJS.precompile emits the render function as ordinary JavaScript source at build time; ship that file like any other script and hand it to hashJS.fromPrecompiled at runtime. Nothing is evaluated in the browser, so the policy stays closed.
// build step
fs.writeFileSync('templates.js',
    'window.T = { row: ' + hashJS.precompile(source) + ' };');

// in the page, under a policy with no unsafe-eval
const row = hashJS.fromPrecompiled(window.T.row);
list.innerHTML = users.map(row).join('');