Templates

JAF's template syntax is called JEX - JavaScript Expressions in HTML.

Write ordinary HTML, and put JavaScript inside curly braces:

source
<script>
let name = "Ada"
let count = 3
</script>

<h2>Hello, {name}!</h2>
<p>You have {count * 2} messages.</p>

That is the whole of it. Anything inside { } is a plain JavaScript expression, and it updates automatically when a variable it reads changes.

Basic Expressions

Use curly braces {expression} for any JavaScript expression:

source
{name}                           <!-- variable -->
{count * 2}                      <!-- math -->
{user.name}                      <!-- property access -->
{formatDate(date)}               <!-- function call -->
{count > 5 ? 'High' : 'Low'}     <!-- ternary -->
{items.length}                   <!-- method/property -->

Attribute Expressions

JEX works in attributes too - and they're reactive:

source
<!-- Pure expressions - quotes optional -->
<a href={url}>Link</a>
<img src={avatarUrl} alt={userName} />
<div class={isActive ? 'active' : 'inactive'}>

<!-- Mixed static + expression - quotes required -->
<a href="/users/{userId}">Profile</a>
<div class="card {isActive ? 'active' : ''}">

Attribute expressions update automatically when referenced variables change.

Conditionals

Use ternary or logical operators. No special {#if} blocks needed:

source
<!-- Ternary for either/or -->
{loggedIn ? <span>Welcome!</span> : <a href="/login">Log in</a>}

<!-- && for show/hide -->
{error && <p class="error">Something went wrong</p>}
{user && <UserCard name={user.name} />}

<!-- In attributes -->
<div class="card {featured ? 'featured' : ''}">

Lists

Use .map() - it's just JavaScript:

source
<ul>
  {items.map(item => <li>{item}</li>)}
</ul>

<!-- With index -->
{items.map((item, i) => <li>{i + 1}: {item}</li>)}

<!-- With objects -->
{users.map(user => <div>{user.name}</div>)}

<!-- Chained methods -->
{users.filter(u => u.active).map(user => <li>{user.name}</li>)}

Keyed Lists

A key tells JAF which row is which, so a row keeps the DOM node it already had. Add one whenever the list can change shape:

source
{users.map(user => <li key={user.id}>{user.name}</li>)}

Without a key, three ordinary things rebuild every row from scratch:

  • Reordering - sorting or dragging rows around rewrites them in place instead of moving them.
  • Inserting in the middle - every row after the new one is rewritten.
  • Re-fetching - fresh objects for the same data look like a completely new list.

A rebuilt row loses whatever the browser was holding in it: focus, text the user had typed into an input, a playing video, scroll position inside the row.

With a key, those rows are moved rather than rebuilt and all of that survives.

The <for> element keys automatically by object identity, so a simple read-only <for> list needs no key.

In .map(), always write the key yourself.

Very Large Lists

Rows arrive at the template as reactive objects, which is what makes rows[5].label = 'x' update one cell.

With thousands of rows, that per-row bookkeeping becomes the most expensive thing on the page. {@static rows.map(...)} turns it off: the rows are handed to the template raw.

Assigning a new list still re-renders as normal. What stops working is in-place mutation - rows[5].label = 'x' no longer updates the cell. See For for the full trade-off.

Protected Regions

Some elements don't process curly braces - they're treated as literal text:

  • <pre> - Preformatted text
  • <code> - Code blocks
  • <style> - CSS rules

This means you can write CSS with curly braces as normal:

source
<style>
.card { padding: 1rem; }  <!-- Not treated as JEX -->
</style>

JEX is Not JSX

It's like React JSX expressions, but in actual HTML files.

JEX JSX
Lives in a .html file, next to its script and style a .jsx or .tsx file
Attributes standard HTML names: class, for DOM property names: className, htmlFor
Events onclick="save()" or onclick={() => save(id)} onClick={save}
Inline style a CSS string: style="color: red", or the style: directive an object: style={{ color: 'red' }}
Several top-level elements no wrapper needed need a fragment: <>...</>

Common mistakes when coming from React:

source
<!-- WRONG: JSX patterns -->
<div className="card">        <!-- Use class, not className -->
<button onClick={handler}>    <!-- Use onclick, not onClick -->

<!-- RIGHT: Standard HTML -->
<div class="card">
<button onclick="handler()">
Remember: JEX is HTML with expressions. Use standard HTML attributes.