Skip to main content

Overview

The Handlebars module provides an extended Handlebars template engine with a rich set of built-in helpers for arrays, strings, values, objects, and control flow.

Registry class

Constructor

Creates a new Handlebars registry with switch helpers pre-registered.
new Registry()
Example:
import { Registry } from "@temelj/handlebars";

const registry = new Registry();

includeAllHelpers

Registers all built-in helpers (array, string, value, object, and core helpers).
includeAllHelpers(): Registry
result
Registry
The registry instance for chaining
Example:
const registry = new Registry().includeAllHelpers();

compile

Compiles a Handlebars template.
compile(source: string, options?: CompileOptions): TemplateDelegate
source
string
required
The template source code
options
CompileOptions
Handlebars compilation options
result
TemplateDelegate
A compiled template function
Example:
const template = registry.compile("Hello {{name}}!");
const result = template({ name: "World" });
console.log(result); // "Hello World!"

render

Compiles and renders a template in one step.
render(source: string, data?: unknown, options?: CompileOptions): string
source
string
required
The template source code
data
unknown
The data context for rendering
options
CompileOptions
Handlebars compilation options
result
string
The rendered output
Example:
const output = registry.render("Hello {{name}}!", { name: "World" });
console.log(output); // "Hello World!"

registerHelper

Registers a single custom helper.
registerHelper(name: string, helper: HelperDelegate): void
name
string
required
The helper name
helper
HelperDelegate
required
The helper function
Example:
registry.registerHelper("uppercase", (str: string) => str.toUpperCase());

const output = registry.render("{{uppercase name}}", { name: "hello" });
console.log(output); // "HELLO"

registerHelpers

Registers multiple helpers at once.
registerHelpers(helpers: HelperDeclareSpec): void
helpers
HelperDeclareSpec
required
Object mapping helper names to helper functions
Example:
registry.registerHelpers({
  uppercase: (str: string) => str.toUpperCase(),
  lowercase: (str: string) => str.toLowerCase()
});

registerPartial

Registers a partial template.
registerPartial(name: string, template: Template): void
name
string
required
The partial name
template
Template
required
The partial template source or compiled template
Example:
registry.registerPartial("header", "<h1>{{title}}</h1>");

const output = registry.render("{{> header}}", { title: "Hello" });
console.log(output); // "<h1>Hello</h1>"

Built-in helpers

Array helpers

array

Creates an array from arguments.
{{array "a" "b" "c"}}

arrayItemAt

Gets an item at a specific index.
{{arrayItemAt items 0}}

arrayContains

Checks if an array contains an item.
{{#if (arrayContains tags "important")}}
  Important!
{{/if}}

arrayJoin

Joins array elements with a separator.
{{arrayJoin tags ", "}}

arrayFilter

Filters an array using a template predicate.
{{#each (arrayFilter users "{{#if active}}true{{/if}}")}}
  {{name}}
{{/each}}

String helpers

camelCase

Converts to camelCase.
{{camelCase "hello world"}} {{!-- helloWorld --}}

snakeCase

Converts to snake_case.
{{snakeCase "helloWorld"}} {{!-- hello_world --}}

pascalCase

Converts to PascalCase.
{{pascalCase "hello world"}} {{!-- HelloWorld --}}

kebabCase

Converts to kebab-case.
{{kebabCase "helloWorld"}} {{!-- hello-world --}}

titleCase

Converts to Title Case.
{{titleCase "hello world"}} {{!-- Hello World --}}

capitalize

Capitalizes the first letter.
{{capitalize "hello"}} {{!-- Hello --}}

upperCase

Converts to uppercase.
{{upperCase "hello"}} {{!-- HELLO --}}

lowerCase

Converts to lowercase.
{{lowerCase "HELLO"}} {{!-- hello --}}

split

Splits a string by a separator.
{{split "a/b/c" "/"}} {{!-- ["a", "b", "c"] --}}

splitPart

Gets a specific part after splitting.
{{splitPart "a/b/c" 1 "/"}} {{!-- "b" --}}

splitPartSegment

Gets a range of parts after splitting.
{{splitPartSegment "a/b/c/d" 1 2 "/"}} {{!-- "b/c" --}}

join

Joins multiple values into a string.
{{join "Hello" " " "World"}} {{!-- "Hello World" --}}

Value helpers

eq

Deep equality comparison.
{{#if (eq value1 value2)}}
  Equal
{{/if}}

ne

Deep inequality comparison.
{{#if (ne value1 value2)}}
  Not equal
{{/if}}

lt / gt / lte / gte

Numeric comparisons.
{{#if (gt age 18)}}
  Adult
{{/if}}

and / or / not

Logical operations.
{{#if (and isActive isVerified)}}
  Verified and active
{{/if}}

{{#if (or isAdmin isModerator)}}
  Has permissions
{{/if}}

orElse

Provides a default value.
{{orElse name "Anonymous"}}

json

Converts to JSON string.
{{json data}} {{!-- Compact JSON --}}
{{json data true}} {{!-- Pretty-printed JSON --}}

isEmpty

Checks if a value is empty.
{{#if (isEmpty array)}}
  No items
{{/if}}

jsValue

Renders a value as JavaScript literal.
const value = {{{jsValue data}}};

Object helpers

object

Creates an object from hash parameters.
{{object name="John" age=30}}

objectPick

Picks specific keys from an object.
{{objectPick user "name" "email"}}

Core helpers

set

Sets variables in the data context.
{{set myVar="value"}}
{{myVar}}

setRoot

Sets variables in the root data context.
{{setRoot globalVar="value"}}
{{@root.globalVar}}

partial

Renders a partial with hash data.
{{partial "header" title="Hello"}}

render

Renders an inline template.
{{render "{{uppercase name}}" name="hello"}}

Switch helpers

Provides switch/case control flow (automatically registered).
{{#switch status}}
  {{#case "active"}}
    Active status
  {{/case}}
  {{#case "pending"}}
    Pending status
  {{/case}}
  {{#default}}
    Unknown status
  {{/default}}
{{/switch}}

Common use cases

Code generation

import { Registry } from "@temelj/handlebars";

const registry = new Registry().includeAllHelpers();

const template = `
interface {{pascalCase name}} {
  {{#each fields}}
  {{camelCase name}}: {{type}};
  {{/each}}
}
`;

const output = registry.render(template, {
  name: "user_profile",
  fields: [
    { name: "first_name", type: "string" },
    { name: "last_name", type: "string" },
    { name: "age", type: "number" }
  ]
});

console.log(output);
// interface UserProfile {
//   firstName: string;
//   lastName: string;
//   age: number;
// }

Configuration templates

import { Registry } from "@temelj/handlebars";

const registry = new Registry().includeAllHelpers();

const template = `
{
  "name": "{{name}}",
  "version": "{{version}}",
  "environment": "{{orElse env \"development\"}}",
  {{#if (gt (split features \",\").length 0)}}
  "features": {{{json (split features \",\")}}}
  {{/if}}
}
`;

const config = registry.render(template, {
  name: "my-app",
  version: "1.0.0",
  features: "auth,api,websockets"
});

Email templates

import { Registry } from "@temelj/handlebars";

const registry = new Registry().includeAllHelpers();

registry.registerPartial("header", `
  <header>
    <h1>{{titleCase title}}</h1>
  </header>
`);

registry.registerPartial("footer", `
  <footer>
    <p>&copy; {{year}} {{company}}</p>
  </footer>
`);

const emailTemplate = `
<!DOCTYPE html>
<html>
  {{> header}}
  <body>
    <p>Hello {{capitalize userName}},</p>
    <p>{{message}}</p>
    {{#if (isEmpty items)}}
      <p>No items to display.</p>
    {{else}}
      <ul>
        {{#each items}}
          <li>{{this}}</li>
        {{/each}}
      </ul>
    {{/if}}
  </body>
  {{> footer}}
</html>
`;

const html = registry.render(emailTemplate, {
  title: "welcome email",
  userName: "john",
  message: "Welcome to our platform!",
  items: ["Feature 1", "Feature 2"],
  year: 2024,
  company: "My Company"
});

API client generation

import { Registry } from "@temelj/handlebars";

const registry = new Registry().includeAllHelpers();

const template = `
export class {{pascalCase serviceName}}Client {
  constructor(private baseUrl: string) {}

  {{#each endpoints}}
  async {{camelCase name}}({{#each params}}{{camelCase name}}: {{type}}{{#unless @last}}, {{/unless}}{{/each}}): Promise<{{returnType}}> {
    const response = await fetch(\`\${this.baseUrl}{{path}}\`, {
      method: "{{upperCase method}}",
      {{#if (eq method "post")}}
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ {{arrayJoin (map params "name") ", "}} })
      {{/if}}
    });
    return response.json();
  }

  {{/each}}
}
`;

const code = registry.render(template, {
  serviceName: "user_service",
  endpoints: [
    {
      name: "get_user",
      method: "get",
      path: "/users/:id",
      params: [{ name: "id", type: "string" }],
      returnType: "User"
    },
    {
      name: "create_user",
      method: "post",
      path: "/users",
      params: [{ name: "name", type: "string" }, { name: "email", type: "string" }],
      returnType: "User"
    }
  ]
});

Build docs developers (and LLMs) love