Learn how to use Template-M's function-based DOM templating in your projects.
Include Template-M in your HTML file:
<script src="/lib/template-m.js"></script>
That's it! Template-M has no dependencies and works directly in the browser.
In Template-M, templates are defined using the data-fn attribute and act as reusable functions. These template functions are extracted from the HTML and can be applied to data.
When you pass JSON data to Template-M, objects with a template property become function applications - they specify which template function to use.
data-fn: Defines a template functiondata-tme: Replaces element content (> for children, < for the element itself)data-tma: Sets element attributesLet's start with a simple example:
<!-- Define a template -->
<template id="greeting">
<div>
<h1 data-tme="> $name"></h1>
</div>
</template>
<!-- Target element -->
<div id="output"></div>
<script>
const data = { name: "World" };
templateM("greeting", data, "output");
</script>
Result: <div><h1>World</h1></div>
The data-tme="> $name" attribute means "replace the children (>) of this element with the value of $name".
The data-tme attribute uses two operators:
> - Replace ChildrenReplaces the element's children with the data value:
<template id="example">
<h1 data-tme="> $title">Default</h1>
</template>
<script>
templateM("example", { title: "Hello" });
</script>
Result:
<h1>Hello</h1>
< - Replace ElementReplaces the entire element with the data value:
<template id="example">
<div>
<span data-tme="< $content">Default</span>
</div>
</template>
<script>
templateM("example", { content: "Hello" });
</script>
Result:
<div>Hello</div>
The <span> element is replaced with just the text "Hello".
Define reusable template functions with data-fn:
<template id="movie">
<div>
<h2 data-tme="> $title"></h2>
<p data-tme="> $metadata"></p>
<!-- Define a "genre" function -->
<div data-fn="genre">
<span>Genre: </span>
<strong data-tme="> $name"></strong>
</div>
</div>
</template>
<script>
const data = {
title: "The Master",
metadata: {
name: "Drama",
template: "genre" // Apply the "genre" function
}
};
templateM("movie", data, "output");
</script>
Result:
<div>
<h2>The Master</h2>
<p>
<div>
<span>Genre: </span>
<strong>Drama</strong>
</div>
</p>
</div>
The metadata object is a function application - its template property specifies which template function to use. The genre function is applied and its result is inserted where $metadata appears.
Template-M automatically handles arrays by using the singular form of the property name as the template:
<template id="repo-list">
<ul>
<li data-fn="repo:$repos" data-tme="> $name"></li>
</ul>
</template>
<script>
const data = {
repos: [
{ name: "template-m" },
{ name: "example-app" },
{ name: "docs" }
]
};
templateM("repo-list", data, "output");
</script>
Result:
<ul>
<li>template-m</li>
<li>example-app</li>
<li>docs</li>
</ul>
The data-fn="repo:$repos" syntax means:
repo - the template function name:$repos - apply this function to the $repos array and insert results hereUse $^propertyName to access properties from parent objects:
<template id="page">
<div>
<h1 data-tme="> $pageTitle"></h1>
<div data-fn="user:$user">
<p>Name: <span data-tme="> $name"></span></p>
<p>Page: <span data-tme="> $^pageTitle"></span></p>
</div>
</div>
</template>
<script>
const data = {
pageTitle: "User Dashboard",
user: { name: "Alice" }
};
templateM("page", data, "output");
</script>
Result:
<div>
<h1>User Dashboard</h1>
<div>
<p>Name: <span>Alice</span></p>
<p>Page: <span>User Dashboard</span></p>
</div>
</div>
The $^pageTitle accesses the parent object's pageTitle property. Use $^^property for grandparent access, and so on.
Combine < (element replacement) with comparisons for conditional rendering:
<template id="menu">
<ul>
<li data-tme="< $selected=home">Home</li>
<li data-tme="< $selected=about">About</li>
<li data-tme="< $selected=contact">Contact</li>
</ul>
</template>
<script>
templateM("menu", { selected: "about" }, "output");
</script>
Result:
<ul>
<li>About</li>
</ul>
Only the "About" item is rendered. Elements where the comparison is false are replaced with an empty DocumentFragment (removed).
You can also use boolean values:
<template id="greeting">
<div>
<p data-tme="< $isLoggedIn">Welcome back!</p>
</div>
</template>
<script>
templateM("greeting", { isLoggedIn: true }, "output");
</script>
Result:
<div>
<p>Welcome back!</p>
</div>
If isLoggedIn is false or null, the <p> element would be removed.
Use data-tma to set element attributes:
<template id="input-form">
<input type="text" data-tma="$placeholder:placeholder">
</template>
<script>
const data = { placeholder: "Enter your name" };
templateM("input-form", data, "output");
</script>
Result:
<input type="text" placeholder="Enter your name">
Separate multiple attributes with semicolons:
<template id="styled-input">
<input data-tma="$placeholder:placeholder;$customStyle:style">
</template>
<script>
const data = {
placeholder: "Enter your name",
customStyle: "border: 2px solid blue"
};
templateM("styled-input", data, "output");
</script>
Result:
<input placeholder="Enter your name" style="border: 2px solid blue">
Use .className to add/remove classes based on boolean values:
<template id="item">
<div data-tma="$isActive:.active">Item</div>
</template>
<script>
templateM("item", { isActive: true }, "output");
</script>
Result:
<div class="active">Item</div>
If isActive is false, the class would not be added.
<template id="button">
<button data-tma="handleClick($id, $name):onclick">Click Me</button>
</template>
<script>
const data = { id: 42, name: "Example" };
templateM("button", data, "output");
</script>
Result:
<button onclick="handleClick(42, "Example")">Click Me</button>
Variables are JSON-stringified and interpolated into the handler string.
Set multiple attributes from an object with :props:
<template id="styled-div">
<div data-tma="$properties">Content</div>
</template>
<script>
const data = {
properties: {
":props": true,
"id": "my-div",
"className": "container active",
"data-value": "123"
}
};
templateM("styled-div", data, "output");
</script>
Result:
<div id="my-div" class="container active" data-value="123">Content</div>
Combine multiple template parts into a single function using / notation:
<template id="user-card">
<div>
<div data-fn="user/1:$user" data-tme="> $name">Name</div>
<div data-fn="user/2" data-tme="> $email">Email</div>
<div data-fn="user/3" data-tme="> $role">Role</div>
</div>
</template>
<script>
const data = {
user: { name: "Alice", email: "alice@example.com", role: "Admin" }
};
templateM("user-card", data, "output");
</script>
Result:
<div>
<div>Alice</div>
<div>alice@example.com</div>
<div>Admin</div>
</div>
All parts (user/1, user/2, user/3) are combined into a single DocumentFragment for the user function.
Use > prefix in data-fn to create templates that only include children:
<template id="file-list">
<dl data-tme="> $files"></dl>
<div data-fn="> file">
<dt data-tme="> $name"></dt>
<dd data-tme="> $value"></dd>
</div>
</template>
<script>
const data = {
files: [
{ name: "readme.txt", value: "Documentation" },
{ name: "index.js", value: "Main file" }
]
};
templateM("file-list", data, "output");
</script>
Result:
<dl>
<dt>readme.txt</dt>
<dd>Documentation</dd>
<dt>index.js</dt>
<dd>Main file</dd>
</dl>
The wrapper <div> is not included - only its children (<dt> and <dd>) are part of the template.
Define a function without placing it in the template:
<template id="list">
<ul>
<div data-tme="> $repos"></div>
<li data-fn="repo" data-tme="> $name"></li>
</ul>
</template>
<script>
const data = {
repos: [{ name: "A" }, { name: "B" }]
};
templateM("list", data, "output");
</script>
Result:
<ul>
<div>
<li>A</li>
<li>B</li>
</div>
</ul>
The <li> with data-fn is extracted as a function template but doesn't create a slot in the main template. The array is rendered using that template and inserted where $repos appears.
templateM(templateId, data, targetElementId)Renders a template and replaces the target element.
templateId: ID of the template elementdata: JSON object to rendertargetElementId: ID of the element to replace (optional)templateMRender(templateId, data)Renders a template and returns a DocumentFragment (without replacing any element).
templateId: ID of the template elementdata: JSON object to render