SkillAgentSearch skills...

VanillaTemplates

A tiny, HTML-first template engine for Vanilla JavaScript. Uses only valid HTML (<var> and data-* attributes) for data binding, loops, conditionals, includes, styles and events. Single-pass rendering, no virtual DOM, no bundler step, usable in the browser and for static site generation.

Install / Use

/learn @Tehes/VanillaTemplates
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Vanilla JS Template Engine

A lightweight and simple JavaScript template engine that uses valid HTML syntax and <var> elements for data binding, offering built-in directives for:

  • Loops with data-loop
  • Conditional rendering with data-if
  • Attribute binding with data-attr
  • Style binding with data-style
  • Event binding with data-event
  • Includes via data-include

It provides a minimalistic solution to dynamically populate HTML templates with JavaScript data, without needing any specialized template language.

Features

  • Valid HTML Syntax: The engine uses standard HTML elements and attributes, with no need for a custom template language or syntax.
  • Data Binding with <var>: Inject JavaScript data into HTML using <var> as placeholders.
  • Loop Support: Iterate over arrays and object maps with data-loop, creating dynamic lists and exposing context helpers.
  • Conditional Rendering with data-if: Show or hide elements based on boolean expressions, including negation for inverse logic.
  • Attribute Binding with data-attr: Dynamically set any HTML attribute (e.g., src, href, alt) from data.
  • Style Binding with data-style: Dynamically set CSS style properties on elements.
  • Event Binding with data-event: Declaratively bind DOM event listeners via an explicit events map.
  • Includes with data-include: Load and render external HTML partials via <var data-include>.
  • Per-Render Include De-Duplication: Within one renderTemplate(...) call, repeated includes with the same src are fetched only once.
  • Nested Loops & Context Helpers: Support nested loops with helper variables _index, _first, _last, _key, and _value for advanced templating scenarios.
  • Single DOM Walk: Perform a single recursive traversal for efficient performance.
  • Zero DOM Footprint: Strip out all <var> placeholders and directive wrappers after rendering, leaving only final HTML.
  • XSS Safety & Error Handling: Inject content via textContent to prevent XSS and throw descriptive errors for invalid bindings.

Why <var>?

HTML already defines the <var> element for “variables” in a broad sense.
In addition to serving as placeholders, <var> elements are also used as directive wrappers for grouping and applying loop (data-loop), conditional (data-if), and include (data-include) logic to multiple nodes. Using it as a placeholder tag brings three advantages:

  1. Valid Mark‑up – Your template stays 100 % HTML; no proprietary braces like {{name}}.
  2. Editor Support – Because it’s a real element, you get syntax highlighting, auto‑closing tags and can nest it anywhere.
  3. Clean Output – During rendering, every <var> (and any <var data-loop> container) is replaced with its resolved content and then removed with replaceWith. The browser never sees the placeholder after hydration.

Tip: Two special cases use empty <var> tags:

  1. Loop container
    A <var> that carries data-loop="items" but no inner text works as the wrapper that gets duplicated. After rendering, the wrapper vanishes – your DOM stays clean.

  2. Primitive array item
    Inside a loop over an array of primitive values (strings, numbers …), leave the inner <var> empty. The engine will substitute it with the current item.

    <var data-loop="user.hobbies">
        <li><var></var></li>
    </var>
    <!-- produces <li>Reading</li> … -->
    

Basic Usage

HTML Template Example

<h2>Hello <var>name</var>!</h2>
<p>You got <var>todos.length</var> To-do(s).</p>

JSON Example

{
    "name": "John Doe",
    "todos": [
        "read documentation",
        "drink green tea"
    ]
}

This will produce:

<h2>Hello John Doe!</h2>
<p>You got 2 To-do(s).</p>

Includes

<!-- Include a user profile partial -->
<var data-include="profile.html"></var>

<!-- Loop over a list of items -->
<ul>
    <var data-loop="items">
        <li><var></var></li>
    </var>
</ul>

API

/**
 * Renders a <template> element into the DOM.
 *
 * @param {HTMLTemplateElement} template - the <template> element to render.
 * @param {object} data - the data object for binding.
 * @param {HTMLElement} target - the container to append rendered content.
 * @param {object} [options]
 * @param {boolean} [options.replace=false] - if true, clears target before rendering.
 * @param {Record<string, Function>} [options.events] - explicit event handlers for data-event.
 */
renderTemplate(template, data, target, { replace = false, events } = {});

// Example usage:
// Append mode:
renderTemplate(tpl, data, target);
// Replace mode:
renderTemplate(tpl, data, target, { replace: true });
// Declarative event binding:
renderTemplate(tpl, data, target, { events: { onSave, onHover, onLeave } });

Data Binding

The engine uses <var> elements to bind data from JavaScript objects to HTML elements. The text inside the <var> tag corresponds to the key or nested key of the object.

Example

<var>user.name</var>
<!-- This will be replaced by the value of user.name from the data object -->

Looping Over Data

To handle arrays, the engine provides a data-loop attribute. When applied to an element, it will iterate over the corresponding array in the data and duplicate the element for each item.

HTML Loop Example

<ul>
    <var data-loop="todos">
        <li><var></var></li>
    </var>
</ul>
<!-- Repeats for each hobby in the user.hobbies array -->

JSON Example

{
    "todos": [
        "Read documentation",
        "Make green tea",
        "Write code",
        "Deploy app"
    ]
}

This will produce:

<ul>
    <li>Read documentation</li>
    <li>Make green tea</li>
    <li>Write code</li>
    <li>Deploy app</li>
</ul>

Looping over Object Properties

data-loop also accepts a plain object.
Inside such a loop you get five helper keys:

| Helper | Meaning | | --------------------------------- | ------------------------------ | | _key | current property name | | _value (or empty <var></var>) | current value (for primitives) | | _index | zero-based counter | | _first | true for the first entry | | _last | true for the last entry |

<dl class="settings-list">
    <var data-loop="settings">
        <dt>
            <strong>#<var>_index</var></strong>
            — <var>_key</var>
            <span data-if="_first">(first)</span>
            <span data-if="_last">(last)</span>:
        </dt>
        <dd><var></var></dd>
    </var>
</dl>
{
    "settings": {
        "theme": "dark",
        "language": "de",
        "itemsPerPage": 20,
        "autoSave": true,
        "welcomeMessage": "Hello, Tino!",
        "maxUploadSizeMB": 50
    }
}

renders as:

<dl class="settings-list">
    <dt>
        <strong>#0</strong>
        — theme <span>(first)</span>:
    </dt>
    <dd>dark</dd>

    <dt>
        <strong>#1</strong>
        — language:
    </dt>
    <dd>de</dd>

    <dt>
        <strong>#2</strong>
        — itemsPerPage:
    </dt>
    <dd>20</dd>

    <dt>
        <strong>#3</strong>
        — autoSave:
    </dt>
    <dd>true</dd>

    <dt>
        <strong>#4</strong>
        — welcomeMessage:
    </dt>
    <dd>Hello, Tino!</dd>

    <dt>
        <strong>#5</strong>
        — maxUploadSizeMB <span>(last)</span>:
    </dt>
    <dd>50</dd>
</dl>

Advanced Example

This example focuses on how the object-map loop exposes _key and _value (an array) so you can nest a second loop over each section’s items.

{
    "sections": {
        "Work Experience": [
            {
                "title": "Senior Software Engineer",
                "company": "Acme Tech Co.",
                "period": "Jan 2021 – Present"
            },
            {
                "title": "Full-Stack Developer",
                "company": "Bright Solutions LLC",
                "period": "Jun 2018 – Dec 2020"
            }
        ],
        "Education": [
            {
                "title": "M.Sc. Computer Science, University of Westshire",
                "period": "Sep 2016 – May 2018"
            },
            {
                "title": "B.Sc. Information Technology, Eastfield College",
                "period": "Sep 2012 – May 2016"
            }
        ]
    }
}
<!-- Outer loop: iterate object map of sections -->
<var data-loop="sections">
    <section>
        <!-- Section title from the key -->
        <h3><var>_key</var></h3>

        <!-- Inner loop: iterate the array in _value -->
        <ul>
            <var data-loop="_value">
                <li>
                    <strong><var>title</var></strong>
                    <var data-if="company"> @ <var>company</var></var>
                    <br>
                    <small><var>period</var></small>
                </li>
            </var>
        </ul>
    </section>
</var>

renders as:

<section>
    <h3>Work Experience</h3>
    <ul>
        <li>
            <strong>Senior Software Engineer</strong> @ Acme Tech Co.<br>
            <small>Jan 2021 – Present</small>
        </li>
        <li>
            <strong>Full-Stack Developer</strong> @ Bright Solutions LLC<br>
            <small>Jun 2018 – Dec 2020</small>
        </li>
    </ul>
</section>

<section>
    <h3>Education</h3>
    <ul>
        <li>
            <strong>M.Sc. Computer Science, University of Westshire</strong>
            <br>
            <small>Sep 2016 – May 2018</small>
        </li>
        <li>
            <strong>B.Sc. Information Technology, Eastfield
View on GitHub
GitHub Stars19
CategoryDevelopment
Updated1mo ago
Forks1

Languages

JavaScript

Security Score

80/100

Audited on Feb 9, 2026

No findings