npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

zzz-template

v1.0.0

Published

Fastest JavaScript template engine using native template literals. Zero dependencies, ~500 bytes, works in Node.js and browser. EJS/Handlebars alternative.

Readme

zzz-template

The fastest, simplest JavaScript template engine with zero dependencies

npm version npm bundle size license

zzz-template is an ultra-lightweight JavaScript template engine that leverages native template literals for maximum performance. A fast, hackable alternative to EJS, Handlebars, and Mustache that works in both Node.js and browsers.

Why zzz-template?

| Feature | zzz-template | EJS | Handlebars | |---------|-------------|-----|------------| | Size (min+gzip) | ~500 bytes | ~6KB | ~17KB | | Dependencies | 0 | 1 | 0 | | Performance | 24M ops/sec | 247K ops/sec | - | | Browser + Node.js | Yes | Yes | Yes | | Template Literals | Native | No | No |

Features

  • Echo variables: ${data.user.name}
  • Layouts: Set layout in child template ${LAYOUT("layout.html")}, echo content ${data.content} in layout.html
  • Include (partial) templates: ${INCLUDE('partial.html', data)}
  • Local variables: ${SET('title', 'Hello world')}, then use it in template: ${local.title}
  • Blazing fast: Matches vanilla JavaScript performance (24M ops/sec)
  • Zero dependencies: No bloat, no supply chain risk
  • Tiny footprint: ~50 lines of code, ~500 bytes minified + gzipped
  • Hackable: Easy to extend with plugins
  • Isomorphic: Works on server (Node.js) and browser

Installation

npm install zzz-template

Compile example (see examples/00-compile)

// file examples/00-compile/example.js
import {ZzzBrowser} from "zzz-template"

const zzz = new ZzzBrowser()
const fn = zzz.compile('Hello ${data.name}') // returns function that renders your template using data: `fn(data)`
console.log(fn({name: 'Jerry'})); // > "Hello Jerry"
console.log(fn({name: 'Tom'})); // > "Hello Tom"

Basic example (browser), render <script> template (see examples/01-basic)

<!-- file examples/01-basic/page.html --> 
<script id="template" type="plain/text">
  <p>
    Hello ${data.name}
  </p>
</script>

<script type="module">
  import { ZzzBrowser } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()

  const result = zzz.render('template', { name: 'world' })

  console.log(result)

  document.body.innerHTML = result
</script>

Basic example (server), render file template (see examples/02-basic)

<!-- file examples/02-basic/template.html --> 
<p>
  Hello ${data.name}
</p>
// file examples/02-basic/example.js
import { ZzzTemplateNode } from 'zzz-template/node.js'

const zzz = new ZzzTemplateNode({ dir: import.meta.dirname })
const result = zzz.render('template.html', { name: 'Jerry' })
console.log(result)
// OUTPUT:
// <p>
//   Hello Jerry
// </p>

Include (partial)

  • useInclude(zzz) to enable include feature
  • ${INCLUDE('partial', {name: 'universe'})} to include partial template

Example include (browser) (see examples/03-include)

<!-- file examples/03-include/page.html --> 
<script type="module">
  import { ZzzBrowser, useInclude } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()
  useInclude(zzz) // 👈 enables include feature

  const result = zzz.render('template', { name: 'world' })

  document.body.innerHTML = result
</script>

<script id="template" type="plain/text">
  <p>
    Hello ${data.name}!
  </p>

  ${INCLUDE('partial', {name: 'universe'})}
</script>

<script id="partial" type="plain/text">
  <p>
    Hey ${data.name}!
  </p>
</script>

Layouts

  • useLayout(zzz) to enable layouts feature
  • ${LAYOUT('layout', {name: 'universe'})} to set layout in template
  • ${data.content} to echo content (result of template) in layout layout

Example Layout (see examples/06-layout)

<!-- file examples/06-layout/layouts.html --> 
<script type="module">
  import { ZzzBrowser, useLayout } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()
  useLayout(zzz) // 👈 enables layout feature

  const result = zzz.render('template', { name: 'world' })

  document.body.innerHTML = result
</script>

<script id="template" type="plain/text">
  ${LAYOUT('layout', {name: 'universe'})}
  <p>
    Hello ${data.name}!
  </p>
</script>

<script id="layout" type="plain/text">
  Hey ${data.name}!
  <div>
    ${data.content}
  </div>
</script>

Layout template itself can set global layout, and global layout can set more global layout, etc. See example how layout can include each other: examples/06-layout/layouts2.html. Please note the example also uses local vars feature.

Example Layout Advanced (see examples/06-layout)

<!-- file examples/06-layout/layouts2.html --> 
<script id="template" type="plain/text">
  ${LAYOUT('layout')}
  ${SET('title', 'My page')}
  <p>
    Hello ${data.name}
  </p>
</script>

<script id="layout" type="plain/text">
  ${LAYOUT('global')}
  <div style="background: #eee; padding: 1em; margin: 1em 0;">
    <div>layout begin</div>
    <section class="layout">
    ${data.content}
    </section>
    <div>layout end</div>
  </div>
</script>

<script id="global" type="plain/text">
  <div style="background: #ffb; padding: 1em;">
    <nav>
      <a href="#">link1</a>
      <a href="#">link2</a>
      <a href="#">link3</a>
    </nav>
    <h1>${local.title}</h1>
    ${data.content}
    <footer>Footer</footer>
  </div>
</script>

<script type="module">
  import { ZzzBrowser, useLayout, useLocal } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()
  useLayout(zzz) // 👈 enables layout feature
  useLocal(zzz) // 👈 enables local vars feature

  const result = zzz.render('template', { name: 'world' })

  console.log(result)

  document.body.innerHTML = result
</script>

Conditions IF

  • useIfMap(zzz) to enable "if, each" feature
  • ${IF(data.n === 42, 'Hello ${data.name}!', {name: 'world'})} to echo string on condition
  • you may want to pass data into template

Example Condition IF (see examples/04-if)

<!-- file examples/04-if/if.html --> 
<script type="module">
  import { ZzzBrowser, useIfMap } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()
  useIfMap(zzz) // 👈 enables "if/map" feature

  const result = zzz.render('template', { name: 'world', n: 42 })

  document.body.innerHTML = result
</script>

<script id="template" type="plain/text">
  <p>
    Hello ${data.name}!
  </p>

  ${IF(data.n === 42, 'Hello ${data.n}!', {n: data.n})}

  <hr>

  ${IF(data.n === 42, `
    Hello ${data.n}! <br>
    ${IF(data.n % 2 === 0, `
      doubled ${data.n} is \${data.number}.
    `, {number: data.n * 2})}
  `)}
</script>

Conditions IFI

  • useIfMap(zzz) to enable "if, each" feature
  • ${IFI(condition, 'template', data)}: if condition then include template with data
  • do not forget to pass data

Example Condition IFI: if (condition) include (see examples/04-if)

<!-- file examples/04-if/ifi.html --> 
<script type="module">
  import { ZzzBrowser, useIfMap } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()
  useIfMap(zzz) // 👈 enables "if/map" feature

  const result = zzz.render('template', { name: 'world', n: 42 })

  document.body.innerHTML = result
</script>

<script id="template" type="plain/text">
  <p>
    Hello ${data.name}!
  </p>

  ${IFI(data.n % 2 == 0, 'partial', {n: data.n})}
</script>

<script id="partial" type="plain/text">
  <p>
    ${data.n} is even!
  </p>
</script>

Iterate, loop, map, for (include template for elements)

  • useIfMap(zzz) to enable "if, each" feature
  • ${MAP('template string', elements)}: for each el in elements render template string w/ el as data

Example MAP (see examples/05-map)

<!-- file examples/05-map/map.html --> 
<script type="module">
  import { ZzzBrowser, useIfMap } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()
  useIfMap(zzz) // 👈 enables "if/map" feature

  const pets = [{ name: 'cat', say: 'meow' }, { name: 'dog', say: 'woof' }]
  const result = zzz.render('template', { name: 'John Doe', pets })

  document.body.innerHTML = result
</script>

<script id="template" type="plain/text">
  // [1] using javascript .map
  <ul>
    ${data.pets.map(x => TEMPLATE('<li>${data.name}</li>', x)).join('')}
  </ul>
  // [2] using MAP and string
  <ul>
    ${MAP(data.pets, '<li>${data.name}</li>')}
  </ul>
  // [2'] using MAP and string template (note that dollar-sign ($) is escaped)
  <ul>
    ${MAP(data.pets, `<li>\${data.name}</li>`)}
  </ul>
  // [3] using MAPI (map include) to include template for each element
  <ul>
    ${MAPI(data.pets, 'pet')}
  </ul>
</script>

<script id="pet" type="plain/text">
  <li>${data.name} (says: ${data.say})</li>
</script>

Extend and hack

ZzzTemplate already has a few built-in plugins. A plugin is just a function that monkey-patches the ZzzTemplate instance. For instance, you can inject your code into the compile function. Here is a 'trim' example:

import {ZzzBrowser, useContentTrim} from "zzz-template"

const zzz = new ZzzBrowser()
useContentTrim(zzz)
const fn = zzz.compile('   Hello ${data.name}   ')
// note that result is trimmed
const result = fn({name: 'Tom'})
console.log(result); // > "Hello Tom"

Here is the code of useContentTrim (built-in)

function useContentTrim(zzz) {
  zzz.e.push('content = content.trim();')
}

This function pushes a code snippet to the end array that will be invoked after the template content is compiled.

Or you may want to introduce a new var in your templates.

import {ZzzBrowser, useContentTrim} from "zzz-template"

const zzz = new ZzzBrowser()

// zzz.s -- s means start (before template content compiled)
zzz.s.push('let $$ = data;') // introduce `$$` for `data`
// zzz.e -- e means end (after template content compiled)
zzz.e.push('content = content.trim();')
const fn = zzz.compile(' Hello ${$$.name} ') // we use new name `$$` for `data`
const result = fn({name: 'Tom'})
console.log(result); // > "Hello Tom"

Example that introduces ESCAPE function that escapes string (see examples/10-extend)

<!-- file examples/10-extend/escape.html --> 
<script type="module">
  import { ZzzBrowser, useFn } from '/zzz-template/index.js'

  const zzz = new ZzzBrowser()

  function escapeHtml (unsafe) {
    return unsafe
      .replaceAll("&", "&amp;")
      .replaceAll("<", "&lt;")
      .replaceAll(">", "&gt;")
      .replaceAll('"', "&quot;")
      .replaceAll("'", "&#039;")
  }

  useFn(zzz, escapeHtml, 'ESCAPE')
  const evilString = 'John<img src="" onerror=alert("Boo!")>'

  const result = zzz.render('template', { name: evilString })

  console.log(result)
  document.body.innerHTML = result
</script>

<script id="template" type="plain/text">
  <p>
    Hello ${ESCAPE(data.name)}!
  </p>
</script>

Fast

Fastest JS engine ever :) That is true, see the benchmark results (ran on the author's old Intel i7):

--------- Benchmark Render ---------
vanilla render x 24,478,910 ops/sec ±1.23% (91 runs sampled)
zzz render x 24,256,470 ops/sec ±1.25% (90 runs sampled)
literal render x 16,843,920 ops/sec ±1.63% (89 runs sampled)
zup render x 2,738,409 ops/sec ±1.43% (91 runs sampled)
ejs render x 247,632 ops/sec ±2.10% (91 runs sampled)
dot render x 1,096,741 ops/sec ±0.58% (93 runs sampled)
edge render x 8,037 ops/sec ±1.80% (90 runs sampled)
Fastest is vanilla render, zzz render

Try to run benchmarks

# go to bench 
cd bench

# install deps
npm i

# run
npm run bench

Security

Do not render templates (by filename) that come from user input, or values in templates that come from user input—it is dangerous. And if you do, please make sure you:

  • provide a secure zzz.read function that reads files from a specified directory, not ../../../secret.passwords
  • escape all user input to prevent XSS attacks

License

MIT

Related

Looking for JavaScript template engines? Here are some alternatives:

  • EJS - Embedded JavaScript templates
  • Handlebars - Semantic templates
  • Mustache - Logic-less templates
  • doT - Fast template engine

Docs revision: 2025-11-30T09:24:44.842Z