templora
v1.3.4
Published
A light weight template engine for nodejs express and fastify application
Maintainers
Readme
Templora
Overview
This is a lightweight js template engine for Node.js that supports:
<% %>JavaScript code blocks{{ }}→ Text and number output{{{ }}}→ Raw HTML outputinclude()→ Partial templateslayout()→ Layouts with nested templates- Global and per-render helpers
- Template caching for performance
It is designed to be simple, flexible, and easy to integrate with Express and Fastify.
Include it in your project:
//ES5
const { renderFile, registerHelper } = require("templora");
//ES6
import { renderFile, registerHelper } from "templora";Express Integration
const express = require("express");
const path = require("path");
const { renderFile, registerHelper } = require("templora");
const app = express();
// Register the engine
app.engine("html", renderFile);
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "html");
// Register a global helper
registerHelper("upper", str => String(str).toUpperCase());
app.get("/", (req, res) => {
const students = [
{ name: "Tom", age: 20 },
{ name: "Alice", age: 25 }
];
res.render("index", {
title: "Home Page",
name: "World",
students,
layout: "main", // layout file without extension
helpers: {
greet: n => `Hello, ${n}!`
}
});
});
app.listen(4000, () =>
console.log("Server running on http://localhost:4000")
);
Fastify intergration
const fastify = require("fastify")();
const path = require("path");
const pointOfView = require("point-of-view");
const { renderFile, registerHelper } = require("gate");
// Register a global helper
registerHelper("upper", str => String(str).toUpperCase());
// Register point-of-view with Gate
fastify.register(pointOfView, {
engine: {
html: renderFile, // Gate engine
},
root: path.join(__dirname, "views"), // template directory
viewExt: "html", // default extension
options: {
// any options passed to renderFile
}
});
// Routes
fastify.get("/", async (request, reply) => {
const students = [
{ name: "Tom", age: 20 },
{ name: "Alice", age: 25 }
];
return reply.view("index", {
title: "Home Page",
name: "World",
students,
layout: "main", // layout path without extension
helpers: {
greet: n => `Hello, ${n}!`
}
});
});
// Start server
fastify.listen({ port: 4000 }, (err, address) => {
if (err) throw err;
console.log(`Server running at ${address}`);
});Features
1. JavaScript Code Blocks
Use <% %> to run arbitrary JavaScript in your templates:
<% for (let i = 0; i < items.length; i++) { %>
<li>{{ i }} - {{ items[i] }}</li>
<% } %>
<!-- -->
<% students.map(student => {%>
{{student.name}}
{{{ include("partials/user", {student}) }}} <!--acces students from the partial-->
<%})%>Supports:
for,while,do…whileif,else if,elseforEach,map- Any valid JavaScript code
2. Variable Interpolation
Escaped Output
- Syntax:
{{ expression }} - Automatically escapes HTML to prevent XSS attacks.
<p>User Name: {{ user.name }}</p>Raw Output
- Syntax:
{{{ expression }}} - Inserts raw HTML without escaping.
<p>HTML Content: {{{ user.htmlContent }}}</p>3. Layouts
You can wrap your template in a layout:
- Layout files can contain
{{{ body }}}for inner template content.
Example layouts/main.html:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
{{{ body }}} OR {{{ main }}}
</body>
</html>4. Includes (Partials)
Include partial templates:
{{{ include("partials/header",{name:'tom'}) }}}- Paths are relative to the current template.
- Variables can be passed as an object.
5. Helpers
Global Helpers
You can register helpers once, and they are available in all templates:
registerHelper("upper", str => String(str).toUpperCase());
registerHelper("lower", str => String(str).toLowerCase());
registerHelper("repeat", (str, n) => String(str).repeat(n));Usage in templates:
<p>{{ upper(user.name) }}</p>
<p>{{ lower(user.name) }}</p>
<p>{{ repeat("-", 10) }}</p>Per-render Helpers
You can also pass helpers per render:
res.render("home", {
data,
helpers: {
shout: s => s.toUpperCase() + "!!!"
}
});- Per-render helpers override global helpers if names collide.
6. Template Caching
- Compiled templates are cached in memory in production for faster rendering but not cached in development.
- The engine automatically reuses compiled functions for repeated renders in production.
Example Template (views/home.html)
<h2>User Information</h2>
<p>Name (escaped): {{ user.name }}</p>
<p>Name (raw): {{ user.name }}</p>
<p>Uppercase (helper): {{ upper(user.name) }}</p>
<h3>Items List</h3>
<ul>
<% items.forEach((item, i) => { %>
<li>{{ i }} - {{ item }} </li>
<% }) %>
</ul>
{{{ include("partials/footer",{item}) }}}
Notes & Best Practices
- HTML Escaping: Use
{{ }}for user input to prevent XSS. Use raw versions only for trusted content. - Helpers: Use global helpers for reusable functions. Per-render helpers are useful for one-off templates.
- Includes & Layouts: Paths are relative to the template file.
- Caching: Templates are cached automatically inproduction, not in development; both restarting the server and refreshing the page reloads them in developement.
- JS Blocks: Any valid JS code is allowed inside
<% %>blocks. Use them for loops, conditionals, and variable manipulation.
Syntax Summary
| Feature | Syntax / Notes |
| -------------------- | --------------------------------------------------------- |
| Escaped variable | {{ var }} |
| Raw HTML variable | {{{ var }}} |
| JS blocks | <% JS code %> |
| Layout placeholder | {{{ main }}} or {{{ main }}} |
| Include partials | {{{ include('relative/path/to/partial', {data}) }}} |
| Global helpers | registerHelper('name', fn) |
| Per-render helpers | helpers option in res.render() |
| Loops & conditionals | <% for(...) { %> ... <% } %> |
| Caching | Enabled in production, auto-reload in dev |
| Optional semicolons | Supported in <% %> blocks |
