Skip to content

The template language

Lexical structure

Names, strings, numbers, comments, keywords and the line rule.

A template is UTF-8 text. It's read as a sequence of tokens: names, strings, numbers, operators and punctuation, separated by whitespace and comments. This chapter defines each kind of token, and the few places where spacing or line breaks matter.

Whitespace and comments

Spaces, tabs and line breaks separate tokens and are otherwise ignored, except where noted under The line rule.

A comment starts with // and runs to the end of the line. There are no block comments.

// A comment on its own line.
p "Hello" // and one after a node

Names

A name starts with an ASCII letter or _, and continues with ASCII letters, digits and _.

A - or a . also continues a name when a letter, digit or _ follows it:

TextTokens
aria-liveone name
post.titleone name: a path, the field title of post
a - ba, -, b
a-bone name, a-b
t->t.donet, ->, t.done

So subtraction needs spaces around the - when its left side is a name.

A name with dots is a path: post.author.name reads post, then its field author, then that one's field name. After ), ] or }, a . is the field operator: first(posts).title, { a: 1 }.a.

Numbers

A number is decimal digits with an optional fraction: 0, 42, 3.25. There are no exponents, no hexadecimal and no separators. All numbers are 64-bit floating point.

A - directly before a digit is part of the number (-1) unless the token before it ends an operand, in which case it subtracts:

TextMeans
x(n: -1)the attribute n is minus one
a -1a minus one
count - 1count minus one
[1, -2]a list of one and minus two

Strings

A string is text between double quotes, on any number of lines.

EscapeMeans
"a double quote
\a backslash
na line break
ta tab
{{ and }}literal braces

Interpolation. {expression} inside a string is replaced by the expression's value as text. Inside the braces, a quote is written \":

p "Hello {user.name ?? \"stranger\"}, you have {count} messages."

How each kind of value becomes text is described under Expressions.

Operators and punctuation

TokenUsed for
( )attribute lists, calls, grouping, lambda parameters
{ }children, records, interpolation
[ ]lists
:attribute and record values
,separating attributes, arguments, items and steps
@events: @click
=declarations
<-setting state in a step
+ - * / %arithmetic, and + joins text and lists
== != < <= > >=comparison
&& || !logic
??a fallback for null
|>piping a value into a call
->lambdas
|record update: { r | a: 1 }
.a field, after ), ] or }

<- needs a space or line break after it. a <- 1 sets a, but a <-1 is a < -1, a comparison.

Keywords

These names are reserved everywhere, and can't be used as names:

KeywordMeaning
if else thenconditionals, in nodes and in expressions
for inloops
true falsebooleans
nullno value

These are keywords only in one place, and are ordinary names everywhere else:

WordA keyword when
state let on loadat the top level, followed by a name (and =, or ( for on)
every afterat the top level, followed by a number, frame, or a name and then when or =
whenin a timer, after its time
frameafter every

So an element can be called on, and a prop can be called state:

on "This element is called on."
p(state: "fine") "So is this prop."

The line rule

Layout is free, with one exception: a string on the same line as an element is that element's text, and a string on a later line is a separate node.

p "This is the paragraph's text."
"This is a text node after the paragraph."

A declaration's steps can continue on the next line after a comma or an operator; see Declarations.

Templates in Rust

Inside template! in Rust, the template is written as Rust tokens and rebuilt from their positions, so the same rules apply. Rust's string literals work as usual, raw strings included, and {{ and }} are literal braces as in any template.