Cheat Sheet
All of JAF's syntax on one page.
Reactivity
| Syntax | Meaning |
let x = 0 | Reactive variable |
const x = 0 | Static constant |
let doubled = count * 2 | Derived value, updates itself |
const doubled = computed(() => count * 2) | Explicit computed, read .value |
effect(() => { ... }) | Side effect, re-runs on change |
Templates (JEX)
| Syntax | Meaning |
{expression} | Text expression |
attr={expression} | Attribute expression |
attr="{static} {expr}" | Mixed attribute |
{cond ? <A /> : <B />} | Conditional |
{cond && <A />} | Show or hide |
{items.map(i => <li>{i}</li>)} | List |
key={item.id} | Keyed list item. Add it whenever the list can reorder |
Directives
| Directive | Example |
class: | class:active={isActive} |
style: | style:color={textColor} |
bind: | bind:value={name} |
on: | on:click|preventDefault={handler} |
ref: | ref:inputRef |
use: | use:autofocus |
transition: | transition:fade, slide, scale, class="..." |
animate:, in: and out: do not exist, and writing one
is a build error. A directive on a component tag is a build error too - the
exception is bind:, which syncs a prop rather than decorating an element. See
the Directives Reference.
bind: by control
| Control | What the variable holds |
<input>, <textarea> | A string |
<input type="number"> | A number, or null while empty |
<input type="checkbox" bind:checked> | A boolean |
<select> | The chosen value |
<select multiple> | An array. Initialise it [] |
| A radio group | One variable across the group, holding the chosen radio's value |
<details>, <dialog> with bind:open | A boolean |
Events
| Syntax | Meaning |
onclick="count++" | Inline expression |
onclick="handleClick()" | Function call |
onclick="handleClick" | Bare name, called for you |
onclick={() => fn(x)} | Arrow: binds arguments, receives the event |
on:click|mod={fn} | With modifiers |
Modifiers: preventDefault · stopPropagation ·
once · self · capture ·
passive
Keys: escape · enter · tab ·
space · up · down ·
left · right · delete ·
backspace · ctrl · alt ·
shift · meta.
There are no letter keys.
Every modifier works wherever a handler compiles, rows and control-flow arms included. A
handler on a component tag - <Button onclick="go()"> - compiles too: it
is your component's code, attached to the child's root element, and never a prop. It needs
the child to have one root element.
Components
| Syntax | Meaning |
<Component /> | Use a component. Filename is the tag name |
<Component prop="value" /> | Static prop |
<Component prop={expr} /> | Dynamic prop |
<Component>content</Component> | With children |
<slot /> | Default slot |
<slot name="x" /> | Named slot |
<div slot="x"> | Fill a named slot |
Built-in components
No import. Both casings work, apart from <Menu>.
| Component | Purpose |
<show> | Conditional, with a fallback |
<for> | Keyed list iteration |
<switch> / <match> | Multi-branch conditional |
<dynamic> | Tag or component chosen at runtime |
<teleport> | Render elsewhere in the DOM |
<suspense> | Boundary for loading content |
<error-boundary> | Catch errors, show a fallback |
<guard> | Access control |
<Layout> | Inherit the parent layout. Must be the whole top-level content of a layout.html; src= names an explicit parent |
<Modal>, <Drawer>, <Popover>, <Tooltip>, <Menu>, <ToastHost> | Overlays |
None of them renders a wrapper element: an arm, a row and a mounted component are inserted
at a comment anchor, so what the page contains is the markup you wrote.
A <for> fallback has its expressions compiled. Every other
fallback - <show>, <switch>,
<suspense>, <error-boundary> - is literal markup, and
an expression or a component tag in one is a build error.
State
| Syntax | Meaning |
let x = storage(default) | Persist to localStorage |
let x = url(default) | Sync with a query param named after the variable |
let x = url('key', default) | Query param with your own key |
let x = path('/docs', default) | Sync with a URL path segment |
let x = unite(ns.var) | Share with another file |
Data fetching
| Syntax | Meaning |
query('/api/x') | Fetch, keyed by URL |
query('key', '/api/x') | Named query, so a mutation can invalidate it |
mutation('/api/x', opts) | Write, with method and invalidates |
q.data · q.loading · q.error | Reactive state |
q.refetch() · m.mutate(input) | Run it |
Lifecycle
| Syntax | Meaning |
<script> | Setup. Runs before the DOM exists |
onMount(() => {}) | After the DOM and its bindings are ready |
onNavigate((path) => {}) | On every route change while mounted |
onDestroy(() => {}) | Cleanup on unmount |
effect(() => {}) | Reactive side effect |
<script once> | Runs once for the whole app |
Routing
| File | Route |
pages/index.html | / |
pages/about.html | /about |
pages/users/[id].html | /users/:id |
pages/layout.html | Layout wrapper |
| Syntax | Meaning |
<a href="/about"> | Navigation. There is no Link component |
route.path · route.params.id · route.query | Current route, reactive |
navigate(path, opts) | Navigate in code. Import from jafjs/router |
UI controller
| API | Meaning |
ui.modal.open(name) · ui.modal.close() | Open and close |
ui.modal.isOpen(name) | Reactive check |
ui.drawer.toggle(name) | Toggle a drawer |
ui.popover.toggle(name, anchor) | Toggle a popover |
ui.toast.show(msg, opts) | Show a toast |
await ui.confirm(msg) | A dialog that resolves |
Styles and script attributes
| Syntax | Meaning |
<style> | Scoped to this component |
<style global> | Not scoped |
<script once> | Run once for the whole app |
<script no-forward> | Do not forward unknown props to the root element |