Guide

The language

Values, reactive state, functions, conditions, loops, errors, imports and raw JavaScript — the core of Kaury.

Kaury is a complete language, not a template syntax: it has variables, functions, loops, errors, modules, and it can run programs without any page (kaury run). It compiles to plain JavaScript.

Blocks and comments

A block is a line followed by lines indented by 2 spaces. No braces, no semicolons.

// a comment
if score > 10
  print "well done"

Values

let tax = 8.1                 // a fixed value: never changes
state count = 0               // a reactive state: the page follows it
let total = price * quantity  // reads a state → recomputed by itself
delivery = 7                  // assignment (creates the variable if needed)
count += 1

Texts are always between double quotes, with {…} interpolation:

let name = "Ada"
print "Hello {name}, you have {count * 2} points"

Numbers can carry a unit: 24px, 2s, 300ms, 50%, 100vh, 90deg, 20/s. Other values: true, false, none, lists [1, 2], objects { name: "Tulip", price: 30 }, ranges 1..5.

Names may contain hyphens: total-price. Write subtraction with spaces: a - b.

Functions

function double x then x * 2        // one line

function total-price cart           // a block: the last line is the result
  sum cart, a -> a.price * a.quantity

Call with or without parentheses: double(4), print total, sum cart, a -> a.price. Anonymous functions: x -> x * 2, (a, b) -> a + b, -> count += 1.

Conditions

if total > 50
  delivery = 0
else if total > 20
  delivery = 4
else
  delivery = 7

let label = if age >= 18 then "adult" else "child"

Comparisons: == != < > <= >=. Logic: and or not. Membership: x in list.

Loops

for p in products
  print p.name
for p, i in products
  print "{i}: {p.name}"
for i in 1..10
  print i
while lives > 0
  lives -= 1

break, continue and return value work as expected.

Lists, texts and objects

let cheap = products.filter(p -> p.price < 20)
let names = products.map(p -> p.name).join(", ")
let sorted = products.sort(p -> p.price)
cart.add product
cart.remove product

Other methods: .find, .contains, .count, .unique(), .take n, .sum fn, .upper(), .lower(), .replace a, b, .split sep, .trim(), .starts-with x… Properties: .length, .first, .last, .keys, .values.

Built-in functions

print, load url, send url, data, sum, average, min, max, round x, 2, random 1, 6, pick list, shuffle, range a, b, now(), format-date d, price 12.5, every 2s, -> …, later 1s, -> …, persist "key", state, copy text, confetti(), vibrate, scroll-to "id", share {…}, json-ld {…}, slug "Hello world", await 2s. See the reference.

Asynchronous code and errors

try
  products = await load "/api/products"
catch e
  print e.message

In a page, await load never blocks the display: the list fills in when the data arrives.

Modules



In card.kaury, export makes a component (or a function) usable by other files:

export component Card name
  card name

Raw JavaScript

When you really need it, a js block is copied as is:

js
  console.log(navigator.userAgent)

French keywords

Every keyword has a French alias, accents optional: soit (let), état (state), si (if), pour (for), titre (title)… Errors follow the language of your system.

Errors that explain

site.kaury:4:10 — "conut" does not exist. Did you mean "count"?
  4 |   button "+1" -> conut += 1
               ^^^^^

Every error has the line, the underlined word and a fix. kaury check --json gives the same errors to editors and AI assistants.

Nothing passes in silence:

  • Habits from other languages get the Kaury form. font-size 20 answers in Kaury: size 20; h1 "Title" answers in Kaury: title "…"; && answers "&&" is written "and". The same goes for div, onClick, =>, ===, var, elif, a : after an option, a forgotten comma.
  • Values are checked. An option that takes a number refuses a word (size large), an option that takes a color refuses what is not one (color FF0000), a function called with too few or too many values says which ones it takes.
  • The pages are rendered. kaury check and kaury build display every page: what only fails at that moment (calling something on an empty value) is reported at its .kaury line, and the build stops instead of publishing a page with a hole in it.

Empty values

none is the empty value. Reading a property of an empty value gives nothing: user.name shows nothing while user is none, and the page fills in when the value arrives. Calling something on an empty value is a mistake, said with its line: test first.

state user = none

page "/"
  title "Hello {user.name}"
  if user
    text user.name.upper()
  button "Log in" -> user = { name: "Ada" }