Templates
Templates are tagged template literals. There is no JSX, no compiler and no runtime parser of your own — the browser’s HTML parser does the work, once.
import { html, render } from '@alacris/core';
render(html`<p>hello</p>`, document.body);Each unique template literal is parsed once per call site into a native
<template> element plus a list of the positions that are dynamic. Rendering it
is cloneNode, and updating is a direct write to the node that changed.
The rule
Section titled “The rule”A function in a ${} is a live binding. A value is written once.
html`<p>${count}</p>` // live — count is a signal, so a functionhtml`<p>${count()}</p>` // a snapshot: you read it, so it is a stringhtml`<p>${() => a() + b()}</p>` // live, derived, updates when either changesThat is the whole model. Everything below is where a ${} can go.
Child positions
Section titled “Child positions”html`<div>${value}</div>`Accepts text, numbers, DOM nodes, other templates, arrays and
each. null, undefined, booleans and '' render nothing, which
makes conditionals read naturally:
html`<div>${() => error() && html`<p class="error">${error}</p>`}</div>`Swapping between two templates reuses the DOM when the template is the same and replaces it when it is not:
html`<div>${() => (loggedIn() ? html`<x-account />` : html`<x-signin />`)}</div>`Attributes
Section titled “Attributes”html`<a href=${url} title=${tip}>link</a>`An attribute is removed entirely when its value is null, undefined or
false. Values can also be spliced into static text:
html`<div class="card card--${variant}">`Properties
Section titled “Properties”Prefix with . to set a DOM property instead of an attribute. Casing is
preserved, which attributes cannot do:
html`<input .value=${text} />`html`<div .innerHTML=${trusted}></div>`html`<x-child .rows=${rows}></x-child>`On a custom element, an unprefixed binding already sets the property, so
rows=${rows} and .rows=${rows} are the same — objects, arrays and camelCase
names round-trip. Use the dot on native elements (.value, .checked) and
when the name should stay an attribute (data-*, aria-*).
Boolean attributes
Section titled “Boolean attributes”Prefix with ?. The attribute is present when truthy and absent otherwise:
html`<button ?disabled=${() => !valid()}>Save</button>`Events
Section titled “Events”Prefix with @:
html`<button @click=${(e) => count(count() + 1)}>+</button>`Handlers are values, never reactive expressions — the function you pass is the handler. Modifiers compose:
html`<button @click.once=${init}>`html`<form @submit.prevent=${save}>`html`<div @click.stop=${select}>`html`<div @scroll.passive=${onScroll}>`.stop, .prevent, .once, .capture and .passive are supported.
class and style
Section titled “class and style”Both accept objects and arrays, so you rarely build these strings by hand:
html`<div class=${{ card: true, 'card--wide': wide(), [size()]: true }}>`html`<div class=${['card', isActive() && 'is-active']}>`html`<div style=${{ width: pct() + '%', '--tone': color() }}>`Custom properties work in the object form, which is the cheapest way to drive CSS from a signal — one property write, no stylesheet re-parse. A key you stop passing is cleared.
Get the element itself:
let input;html`<input ref=${(el) => (input = el)} />`Spreading is not supported
Section titled “Spreading is not supported”There is no ${...props} in a tag. Set properties explicitly, or use a ref
and assign them.
Use the svg tag for fragments that belong inside an <svg>, so children get
the right namespace:
import { svg } from '@alacris/core';
svg`<circle cx="10" cy="10" r=${radius} />`render
Section titled “render”For anything outside a component. Returns a disposer that removes the DOM and stops every binding:
import { html, render } from '@alacris/core';
const dispose = render(html`<p>${count}</p>`, document.querySelector('#app'));dispose();What you cannot do
Section titled “What you cannot do”Bindings go in attribute or child positions. These are not supported, and the compiler throws rather than guessing:
html`<${tag}>` // dynamic tag nameshtml`<div ${attrName}="x">` // dynamic attribute nameshtml`<textarea>${value}</textarea>` // use .value insteadAn attribute with a binding must be quoted or whole — class="a ${b}" or
class=${b}, not class=a${b}.
- Lists — the part that decides whether a list scales
- Template syntax reference — every form on one page