Home Documentation Templates Examples Showcase GitHub ↗
Theme

Core reference · Pure value evaluation

Expressions.

Nift uses one pure expression model in both $[expression] and @if(expression). The operators, precedence, value lookup, type rules and short-circuit behaviour are the same; only the surrounding context decides what happens to the result.

One expression language, two common contexts.

$[...] evaluates an expression and renders its scalar result. @if(...) evaluates the same kind of expression and applies Nift truthiness to decide whether its branch renders. You do not need to learn a separate condition grammar.

The same expression in $[...] and @if(...)

$[price * quantity]

@if(price * quantity >= 100){
    <p>Large order</p>
}

The first expression renders a derived numeric value. The second uses the same arithmetic and comparison rules to produce a boolean condition.

$[post.published && !post.draft]

@if(post.published && !post.draft){
    ...
}

In $[...], that logical expression renders true or false. In @if(...), the resulting value controls the branch.

Values and lookups

A plain value lookup is the simplest expression:

$[title]
$[post.author.name]
$[posts[2].title]
$[loop.index]
$[paginate.current]

Expression values can come from built-in page/build metadata, JSON bindings, project contracts, loop bindings and pagination metadata. Existing namespace and scope rules still apply; expressions do not introduce assignment, mutable variables or new bindings.

Arithmetic

Numeric expressions support binary +, -, *, / and %, plus unary +/- and parentheses.

$[2 + 3 * 4]          // 14
$[(2 + 3) * 4]        // 20
$[-2 + 5]             // 3
$[10 / 4]             // 2.5
$[10 % 3]             // 1
$[loop.index + 1]
$[price * quantity]
Arithmetic is numeric only.

+ does not concatenate strings and Nift does not coerce strings, booleans, null, arrays or objects into numbers. Use ordinary template composition for text, for example Hello $[name].

Division or modulo by zero is a build error. Modulo requires integer-valued operands; fractional modulo is rejected. Non-finite arithmetic results are rejected rather than emitted into generated output.

Comparisons

Expressions support ==, !=, <, <=, > and >=.

$[score >= 50]
@if(item.priority >= site.minimum_priority){ ... }
@if(item.type == "article"){ ... }

Numbers order numerically and strings lexicographically. Ordering requires two numbers or two strings; Nift does not coerce between types. Booleans, null, arrays and objects are not orderable.

Logic and short-circuiting

Use !, && and || to compose conditions. Logical operators short-circuit: an operand that cannot affect the result is not resolved.

@if(post.published && !post.draft){ ... }
@if((post.featured || post.pinned) && post.published){ ... }

$[post.published && !post.draft]

This matters for correctness as well as convenience: a skipped logical operand is not needlessly resolved.

Precedence and parentheses

Nift follows conventional precedence. From tightest to loosest:

LevelOperators
Unary!, unary +, unary -
Multiplicative*, /, %
Additive+, -
Comparison==, !=, <, <=, >, >=
Logical AND&&
Logical OR||
Conditional?:
$[2 + 3 * 4]       // 14
$[(2 + 3) * 4]     // 20
@if((a || b) && c){ ... }

Use parentheses whenever they make the intended grouping clearer; there is no penalty for being explicit.

Truthiness in @if(expression)

@if does not require the expression to be a comparison. It evaluates the expression and applies Nift's documented truthiness rules, so direct boolean/value conditions remain valid alongside arithmetic and comparisons.

@if(post.published){ ... }
@if(!site.disabled){ ... }
@if(loop.index % 2 == 0){ ... }

See Loops & conditions → for the complete truthiness table and branch syntax.

Lazy ternary rendering

$[condition ? true-branch : false-branch] uses the same expression evaluator for its condition. The shorthand $[condition ? true-branch] is equivalent to an empty false branch.

$[score >= 50 ? 'pass' : 'fail']
$[loop.index % 2 == 0 ? ' even' : ' odd']
$[post.featured ? @input('partials/featured-badge.html')]

Ternary branches are deliberately lazy and are rendered as ordinary Nift source. Only the selected branch is parsed; the unselected branch does not resolve values, execute directives, register dependencies/requirements or fail because of something that only exists in that branch.

Pure means no mutation

Expressions derive values from data Nift already knows. They do not turn the template language into a scripting runtime.

SupportedNot part of expressions
Value lookup, arithmetic, comparisons, logic, parentheses, ternary selectionAssignment, mutable variables, ++/--, user-defined functions, arbitrary system execution

Expressions inside other Nift features

Whenever an existing feature consumes a $[...] scalar, that scalar can be derived by an expression. Pagination also has explicit relative page syntax:

@pathtopage($[paginate.previous])
@pathtopage(+$[offset])
@pathtopage(-$[offset])
@pathtopage(+$[offset * 2])

@pathtopage(n) ultimately requires an integer page number or integer offset; expression support does not make fractional page numbers valid.