Coming from Handlebars, Mustache, or Liquid¶
kBars deliberately mixes syntax from all three engines — Mustache's tag shape, Handlebars' block
helpers and ..//@index vocabulary, Liquid's | filter pipeline — rather than inventing its own.
If you already know one of them, most of kBars is recognizable on sight. This page is a fast map from
what you already know to what kBars calls it, so you can skip re-deriving the syntax from scratch.
It is not a migration tool: kBars does not execute Handlebars, Mustache, or Liquid templates unmodified, and porting real templates still means checking the pages linked throughout this table against your source engine's docs.
What carries over¶
| Syntax | Where you've seen it | In kBars |
|---|---|---|
{{ name }} |
Mustache, Handlebars | Identical — escaped interpolation. See Interpolation. |
{{{ name }}} |
Mustache, Handlebars | Identical — unescaped interpolation. Liquid has no triple-brace form; its filters are the closest equivalent to opting out of escaping (see below). |
{{! comment }} |
Mustache, Handlebars | Identical shape and meaning — produces no output. |
{{#if x}}…{{else if y}}…{{else}}…{{/if}} |
Handlebars | Identical, including the else if chain. See Blocks. |
{{#each items}}…{{else}}…{{/each}} |
Handlebars | Identical name and {{else}}-on-empty behavior. See Blocks. |
{{#with obj}}…{{/with}} |
Handlebars | Identical — pushes obj as the new context for the body. |
../foo, ../../foo |
Handlebars | Identical — walks up the context stack one frame per ../. See Interpolation. |
@index / @key |
Handlebars | Identical names and meaning inside {{#each}}. |
{{> key }} |
Mustache, Handlebars | Same tag; how key resolves to a template is configured by whoever builds the application, not baked into the template source. See Partials. |
{{{{raw}}}}…{{{{/raw}}}} |
Handlebars | Identical four-brace raw block. |
~ whitespace trimming ({{~ x }}, {{ x ~}}) |
Handlebars | Identical marker and placement rules. See Template Basics. |
Standalone-line whitespace stripping (a tag alone on its line has the line's surrounding whitespace removed, even with no ~) |
Mustache | Identical rule, layered underneath the ~ markers above. |
\{{ literal }} |
Handlebars | Identical — a backslash before {{ escapes it to literal text. |
\| filter/transform pipeline ({{ name \| upcase }}) |
Liquid | Same pipe syntax and left-to-right chaining. See Transforms. Handlebars has no pipe operator at all — see below. |
{{#*inline "name"}}…{{/inline}} inline partials |
Handlebars | Same block-scoped definition-then-invoke behavior, but the name is an unquoted identifier and there is no * — kBars spells it {{#inline name}}…{{/inline}}. See Partials. |
{{#> partial}}…{{/partial}} partial blocks |
Handlebars | Only the failover half: a resolution miss renders the block body as fallback content, same as Handlebars. kBars spells the close {{/>}} (bare) or {{/> partial}} (named, must match the open key) rather than reusing the block name; there is no {{> @partial-block }} re-entry. See Partials. |
What's renamed¶
| Handlebars / Mustache / Liquid | kBars | Difference beyond the name |
|---|---|---|
Liquid filters ({{ x \| upcase }}) |
Transforms | Same pipe syntax; kBars's catalog (46 built-ins across strings/math/lists/date-times) is its own, not Liquid's — check the transform family pages rather than assuming a Liquid filter name and behavior carry over unchanged. |
Handlebars helpers ({{formatDate date "YYYY"}}, prefix-call syntax) |
Transforms, but postfix-pipe only | There is no prefix-call syntax in kBars — everything a helper would do goes through \| name: arg instead. A custom transform is registered by whoever builds the application you're writing templates for. |
Mustache inverted section ({{^x}}…{{/x}}) |
{{#unless x}}…{{else}}…{{/unless}} |
Same "renders when falsy" meaning, spelled as a distinct block tag (Handlebars-style) instead of Mustache's ^ sigil on a section. |
Mustache section ({{#x}}…{{/x}}, overloaded for both truthy-conditional and list iteration and context-push) |
{{#if x}}, {{#each x}}, {{#with x}} |
kBars splits Mustache's one overloaded tag into three explicit ones (Handlebars' approach) — a struct value under {{#x}} in Mustache implicitly pushes context the way {{#with}} does in kBars; a list implicitly iterates the way {{#each}} does. |
Liquid {% for item in list %} |
{{#each list}} |
kBars has no in clause or loop variable name — the loop body reads the current element via this/an identifier, and the element itself becomes the pushed context (Handlebars' model), not a named loop variable (Liquid's model). |
Liquid forloop.index (1-based), forloop.index0, forloop.first, forloop.last |
@index (0-based) |
kBars has no @first/@last — test @index == 0 (or use a \| size comparison) instead. |
Liquid {% assign x = … %}, {% capture x %}…{% endcapture %} |
{{= @env/x = … }}, {{= @local/x = … }} |
kBars has no bare/global assignment target — every binding is namespaced to @env (render-global, survives block boundaries) or @local (frame-scoped, dropped when its block ends). See Scoped Variables. |
Liquid {% include 'name' %} / {% render 'name' %} |
{{> key }} |
kBars partials take at most one context argument ({{> key ctx }}), not Liquid's named-parameter list ({% render 'name', foo: bar %}). |
Liquid {% raw %}…{% endraw %} |
{{{{raw}}}}…{{{{/raw}}}} |
Same purpose, Handlebars' four-brace spelling instead of Liquid's tag. |
Liquid {%- x -%} / {{- x -}} hyphen trimming |
{{~ x ~}} tilde trimming |
Same idea (trim adjacent template-source whitespace), Handlebars' ~ token instead of Liquid's -. |
What's deliberately absent¶
| Feature | Where it exists | Why kBars doesn't have it |
|---|---|---|
| Block helpers — a registered function that receives the unrendered block body and decides whether/how many times to render it | Handlebars | kBars's block set (if/unless/each/with) is fixed and built in; a custom transform can only post-process an already-resolved value, never control block rendering. There is no registration hook equivalent to Handlebars.registerHelper for block-level control. |
Subexpressions — (helper arg) nested anywhere a helper argument is expected |
Handlebars | kBars allows a parenthesized expression chain as a transform argument (\| append: (title \| upcase)) and as an operand of a binary expression (a and (b or c); see Expressions), but not as a general nesting mechanism anywhere a helper call would go — there is no helper-call syntax to nest inside. |
| Lambdas — a context value that is itself a function, invoked with the section's unrendered text | Mustache | Not supported; every kBars context value is data, never executable. |
Partial parameters ({{> partial param=value}}) |
Handlebars | kBars partials take at most one context argument — no named parameters. See Partials. |
| Global mutable assignment — a binding with no explicit scope, visible everywhere for the rest of the render | Liquid ({% assign %} is render-global by default) |
kBars requires every {{= }} target to name @env or @local explicitly — there is no unscoped third option. |
Unparenthesized multi-operator chaining — {% if a and b or c %} with no grouping |
Liquid | A kBars expression is a single operand or exactly one binary operator; combining more than one operator always requires parentheses around one side, e.g. {{#if a and (b or c) }} — see Expressions. There is no implicit operator precedence to fall back on. |
Scientific-notation number literals in source (1e10) |
Liquid, Handlebars (as JS numbers) | A kBars number literal is -?[0-9]+(\.[0-9]+)? — no exponent syntax. Rendered output still uses full ECMA-262 formatting for values that arrive already large — see Number Rendering. |
The truthiness trap¶
If you're porting from Liquid specifically, this is the difference most likely to silently change
behavior: Liquid treats 0, "" (empty string), and an empty array as truthy — only nil and
false are falsy. kBars's rule (shared with Handlebars) is stricter: {{#if}}/{{#unless}} and
and/or treat null, numeric 0, false, "", and an empty list as falsy; everything else —
including an empty struct {} — is truthy. A Liquid template relying on {% if count %} being true
for count = 0 needs an explicit comparison in kBars: {{#if count != 0}}. See
Expressions for the full rule.
Where to next¶
- Writing Templates — the full page-by-page tour, if you'd rather read start to finish than jump in from a comparison table.
- Template Basics — delimiters, comments, raw blocks, whitespace trimming, all from scratch rather than by comparison.
- Custom Transforms — the closest kBars equivalent to a Handlebars helper or Liquid filter, worked end to end, if you're also building the application.