Reference · compiler 1.17.0 · syntax 1.4.1 · output 1.8.2

API Reference

The complete public surface: what a component script can call, what a template can express, what every compiled module exports, and the compiler, router, and server entry points behind them.

Script globals

Available inside every component <script> with no import — the compiled module provides them.

onMount()

lifecycle component script
onMount(callback: () => void): void

Registers a hook that runs once, client-side, after mount. The right place for browser APIs, timers, observers, fetches, and anything that reads window, document, or the URL.

ParameterTypeDescription
callback() => voidRuns once on the client after the component is attached, its children are mounted, and the initial update has completed. May be async.

Returnsvoid

Status.wizz
<script>
  let status = "connecting";

  onMount(() => {
    status = "ready";   // reactive: flows through the scheduler
  });
</script>

<main><p>{status}</p></main>

onDestroy()

lifecycle component script
onDestroy(callback: () => void): void

Registers a teardown hook. Remove global listeners, clear timers, disconnect observers, and abort in-flight requests here. Do not change state — the component is going away.

ParameterTypeDescription
callback() => voidRuns when destroy() is called — before child components are destroyed and the root is removed.

Returnsvoid

Width.wizz
<script>
  let width = 0;

  function measure() {
    width = window.innerWidth;
  }

  onMount(() => {
    measure();
    window.addEventListener("resize", measure);
  });

  onDestroy(() => {
    window.removeEventListener("resize", measure);   // yours to remove
  });
</script>

<p>{width}px</p>

persist()

state marker component script
let name = persist(key: string, defaultValue: any);

Marks a top-level let as persisted: read from localStorage at mount, written on every statement mutation, and synchronized across components and tabs via BroadcastChannel (storage events as fallback). Server renders use the default.

ParameterTypeDescription
keystring literalThe localStorage key, used verbatim. Components of one origin share one namespace.
defaultValueJSON valueUsed when nothing is stored, storage is unreadable, and during server rendering.

ReturnsThe stored value, or defaultValue — as an ordinary reactive variable.

ThemeToggle.wizz
<script>
  let theme = persist("theme", "light");

  function toggle() {
    theme = theme === "light" ? "dark" : "light";   // writes + broadcasts
  }
</script>

<button on:click={toggle}>Theme: {theme}</button>

export let

prop component script
export let name = defaultValue;

Declares a reactive, read-only prop. Parents pass it as an attribute; updates arrive through setProps() without remounting. Assigning a prop in the child is a compile error (WIZZ-G015).

ParameterTypeDescription
nameidentifierThe prop name. One prop per declaration; may not start with the reserved __wizz prefix.
defaultexpression?Optional default, used when the parent omits the prop or passes undefined.
Badge.wizz
<script>
  export let label = "Untitled";
  export let count;               // undefined unless passed
</script>

<p>{label}: {count}</p>

<!-- parent -->
<Badge label="Inbox" count={unread} />

Template syntax

Expressions in {...} accept identifiers, member access, string and number literals, and strict equality. Bind attributes with an unquoted expression as the whole value.

on:event

directive native elements
<element on:event={handler}>

Attaches a native listener with addEventListener. Client-only; removed automatically on destroy. Not allowed on component tags (WIZZ-G011) or inside {#each} bodies (WIZZ-G006) — delegate from the container instead.

ParameterTypeDescription
eventDOM event nameAny native event: click, input, submit, keydown, pointermove…
handleridentifier | (event) => …A component function, or an expression whose value is passed to addEventListener.
directives
<input on:input={handleInput} />
<button on:click={(event) => add(event.detail)}>Add</button>
<ul on:click={handleListClick}>{#each rows as row (row.id)}<li data-id={row.id}>{row.label}</li>{/each}</ul>

{#if} … {:else} … {/if}

block template
{#if condition} … {:else} … {/if}

Chooses a branch at mount time. Changing the condition later does not swap an already-rendered branch, so drive interactive show/hide with a bound class.

ParameterTypeDescription
conditionexpressionIdentifier, member access, literal, or strict equality (===).
branch
{#if status === "ready"}
  <p>Ready.</p>
{:else}
  <p>Loading…</p>
{/if}

{#each} … {/each}

block template
{#each items as item (key)} <row /> {/each}

Renders one native root element per item, inside a native parent. Bodies may not contain components, on: directives, or nested blocks.

ParameterTypeDescription
itemsreactive letThe collection. A const renders once at mount; a let updates on reassignment.
itemidentifierName for the current element inside the body.
(key)expression?Optional unique key. Keyed blocks retain and move nodes by key; keyless blocks reconcile by index. Duplicate keys throw.
list
<ul class="users">{#each users as user (user.id)}<li class={user.rowClass}>{user.name}</li>{/each}</ul>

<wizz:head>

block component root
<wizz:head> <title/> <meta/> <link/> </wizz:head>

Per-component document metadata. Server-rendered into the head and adopted on hydration; client mounts prepend tagged nodes and destroy() releases them. Multiple titles resolve last-in-tree-wins.

ParameterTypeDescription
children<title> | <meta> | <link>The only permitted elements. Expressions evaluate at render time; on: directives are skipped.
head
<wizz:head>
  <title>{post.title} — Blog</title>
  <meta name="description" content={post.summary}>
  <link rel="canonical" href={post.url}>
</wizz:head>

<wizz:style>

block component root
<wizz:style> /* raw CSS */ </wizz:style>

Component-scoped CSS. Delivered once per document in dev, adopted on hydration, refcounted on client mounts, and extracted to dist/app.css by wizz build. Expressions are never interpolated into CSS.

ParameterTypeDescription
cssraw CSSScoped by a data-wizz-s attribute on each selector's terminal element; @keyframes names gain the scope suffix; @media is descended.
styles
<wizz:style>
  .card { padding: 1rem; }
  .card:hover h2 { color: var(--accent); }
  @keyframes pulse { 50% { opacity: 0.6; } }
</wizz:style>

Generated modules

Each component compiles to Name.js; server-renderable components also get Name.server.js and Name.hydrate.js. These surfaces are covered by the output version.

mountComponent()

default export Name.js
export default function mountComponent(target: Node, props?: object): { setProps?(next: object): void; destroy(): void }

Creates the component's DOM, appends its root to target, runs the initial update and mount hooks, and returns a handle. setProps exists only when the component declares props.

ParameterTypeDescription
targetNodeA live DOM node; the component's root is appended to it.
propsobject = {}Initial values for declared props.

Returns{ setProps?, destroy } — call destroy() exactly once.

manual mount
import mountComponent from "/components/Counter.js";

const handle = mountComponent(document.getElementById("slot"), { label: "Clicks" });
handle.setProps?.({ label: "Taps" });
handle.destroy();   // exactly once

hydrateComponent()

named export Name.hydrate.js
hydrateComponent(target: Element, props: object, state: object): Handle

Adopts server-rendered markup in place: verifies tags, data-wizz-id attributes, and text against the template, then attaches listeners. On any mismatch it warns once, clears target, and mounts fresh.

ParameterTypeDescription
targetElementThe mount point that already contains the server-rendered markup.
propsobjectThe same props the server rendered with.
stateobjectThe parsed application/wizz-state payload.

ReturnsThe same handle shape as mountComponent().

manual hydration
import { hydrateComponent } from "/pages/Home.hydrate.js";

const script = document.querySelector('script[type="application/wizz-state"]');
const state = JSON.parse(script.textContent);
script.remove();

const handle = hydrateComponent(document.getElementById("app"), {}, state);

hydrateRoot()

named export Name.hydrate.js
hydrateRoot(rootNode: Element, props: object, state: object): Handle

Adopts the given node itself as the component root. Parents use it for nested hydration; a structural mismatch remounts inside the child's root without detaching the parent.

ParameterTypeDescription
rootNodeElementThe exact element to adopt as the component root.
propsobjectProps for the component.
stateobjectThe component's state slice (for children: state.__wizz.components[id]).

ReturnsThe component handle.

nested hydration
// Used by parents during nested hydration; available on every hydratable module.
import { hydrateRoot } from "/components/Card.hydrate.js";

const handle = hydrateRoot(cardElement, { title: "Intro" }, cardState);

renderComponent()

named export Name.server.js
renderComponent(props?: object, options?: object): { html: string; head?: string; state: object }

Renders escaped HTML without a DOM. Event handlers and hooks are skipped; author scripts run, so browser globals at the top level throw.

ParameterTypeDescription
propsobject = {}Props to render with.
optionsobject = {}Present when the component or a child declares <wizz:head>; carries the head owner path.

Returns{ html, head?, state }

server render
import { renderComponent } from "./dist/pages/Home.server.js";

const { html, head, state } = renderComponent({});
// html:  escaped markup, no DOM involved
// head:  serialized head markup (only when <wizz:head> is declared)
// state: reactive state snapshot, children under state.__wizz.components

serializeInitialState()

named export Name.server.js
serializeInitialState(state: object): string

Serializes state into an inline application/wizz-state script with every '<' escaped. Deliver it as a sibling of the mount point, never inside it.

ParameterTypeDescription
stateobjectThe state returned by renderComponent().

ReturnsAn HTML string.

delivery
import { renderComponent, serializeInitialState } from "./dist/pages/Home.server.js";

const { html, state } = renderComponent({});
const body = '<div id="app">' + html + "</div>" + serializeInitialState(state);
// '<script type="application/wizz-state">{"count":0}</script>' — every "<" escaped

Compiler API

Exported from src/compiler (CommonJS). Most applications never call it directly — wizz build and wizz dev do.

compile()

function src/compiler
compile(source: string, options?: { filePath?, hydratable?, diagnostics? }): { source, payload, sourceMap, version, diagnostics? }

Compiles a component to a mountable ES module. Errors are SyntaxErrors carrying a stable code, line, column, and (with filePath) a caret code frame.

ParameterTypeDescription
sourcestringThe .wizz component source.
options.filePathstring?Qualifies errors with the path and adds a caret code frame. Always pass it for files.
options.hydratableboolean?Restrict to the server-renderable surface and also export hydrateComponent / hydrateRoot.
options.diagnostics'collect'?Return { diagnostics: [...] } records instead of throwing.

Returnssource (module text), payload (analyzed AST), sourceMap (v3 or null), version (frozen triple).

build tooling
const { compile } = require("./src/compiler");

const { source, payload, sourceMap, version } = compile(componentSource, {
  filePath: "src/pages/Home.wizz"
});

try {
  compile("<main></section>");
} catch (error) {
  error.code;      // "WIZZ-P018" — mismatched closing tag
  error.line;      // structured location
}

const result = compile("<main><p>hi", { filePath: "src/App.wizz", diagnostics: "collect" });
result.diagnostics[0].code;   // "WIZZ-P022"

compileServer()

function src/compiler
compileServer(source: string, options?: object): { source, payload, sourceMap: null, version }

Shares the parse → analyze → assign pipeline and emits a DOM-free server module. Component tags render through each child's own server module, gated by the eligibility options.

ParameterTypeDescription
sourcestringThe .wizz component source.
options.filePathstring?As for compile().
options.diagnostics'collect'?As for compile().
options.componentServerRenderableRecord<string, true>Import names whose own server builds compile; a missing entry rejects that tag.
options.componentIneligibilityReasonsRecord<string, string>Child failure reasons, chained into the diagnostic as 'Underlying reason: …'.

ReturnsThe same shape as compile(), with sourceMap always null.

server target
const { compileServer } = require("./src/compiler");

const { source, sourceMap } = compileServer(componentSource, {
  filePath: "src/components/Counter.wizz"
});
// source exports renderComponent() and serializeInitialState(); sourceMap is always null

VERSIONS

constant src/compiler
VERSIONS: Readonly<{ compiler: string; syntax: string; output: string }>

The frozen compatibility triple, also stamped on the first line of every generated module and printed by wizz --version.

versions
const { VERSIONS } = require("./src/compiler");

VERSIONS;
// { compiler: "1.17.0", syntax: "1.4.1", output: "1.8.2" }  (frozen)

Runtime

createRouter()

function runtime/router.js
createRouter({ routes, hydratableRoutes?, target, window, document }): { render(), navigate(pathname), destroy() }

The client router used by runtime/main.js. Exact pathname matching, first-render hydration, lazy imports, race-safe renders, and a popstate listener.

ParameterTypeDescription
routesRecord<path, () => Promise<module>>Lazy client module loaders keyed by exact pathname.
hydratableRoutesRecord<path, () => Promise<module>>Optional hydratable loaders, used only for the first render.
targetElementThe mount point (#app).
window / documentWindow / DocumentInjected globals, which keeps the router testable.

Returns{ render(): Promise, navigate(pathname): Promise, destroy(): void }

custom entry
import { createRouter } from "/runtime/router.js";

const router = createRouter({ routes, hydratableRoutes, target, window, document });
await router.render();             // match location.pathname, hydrate or mount
await router.navigate("/docs");    // pushState + render
router.destroy();                  // remove popstate listener + current page

handler(request)

default export src/server/api/*.js
export default async function handler(request: Readonly<Request>): unknown

Server-side route handler. Plain value → 200 JSON, string → text, undefined → 204, { status, headers, body } → full control, throw → fixed 500.

ParameterTypeDescription
request.methodstring'GET', 'POST', …
request.pathstringDecoded pathname.
request.queryURLSearchParamsParsed query string.
request.headersobjectLowercased header names.
request.bodystring | nullRaw body text (max 1 MB, else 413).
request.json()() => Promise<any>Parses the body; malformed JSON answers 400.

ReturnsSee the return-value table in Server APIs & SSR.

src/server/api/health.js
// src/server/api/health.js → /api/health
export default async function handler(request) {
  return { ok: true, method: request.method };   // 200 JSON
}

Diagnostic codes

Every author-facing compile failure carries a stable code. Codes are never reworded into a different meaning or reused, so tooling can switch on them. WIZZ-A### is reserved and empty.

Parse stage — WIZZ-P

CodeCondition
WIZZ-P001Template tokenizer error
WIZZ-P002Malformed {#each} syntax
WIZZ-P003Unexpected {/each}
WIZZ-P004Unexpected {/if}
WIZZ-P005Unexpected {:else}
WIZZ-P006Disallowed child inside <title>
WIZZ-P007Disallowed element inside <wizz:head>
WIZZ-P008Element inside <wizz:style> (raw CSS only)
WIZZ-P009Attributes on <wizz:head>
WIZZ-P010Nested <wizz:head>
WIZZ-P011Attributes on <wizz:style>
WIZZ-P012Nested <wizz:style>
WIZZ-P013Duplicate <wizz:head>
WIZZ-P014Duplicate <wizz:style>
WIZZ-P015Block directive inside <wizz:head>
WIZZ-P016Expression directly inside <wizz:head>
WIZZ-P017Expression inside <wizz:style>
WIZZ-P018Mismatched closing tag
WIZZ-P019Only <title>, <meta>, <link> allowed in <wizz:head>
WIZZ-P020Plain <style> — use <wizz:style>
WIZZ-P021Closing tag on a void element
WIZZ-P022Unclosed tag
WIZZ-P023Unexpected closing tag
WIZZ-P024Unsupported token type
WIZZ-P025Unclosed string in expression
WIZZ-P026Unexpected character in expression
WIZZ-P027Unparseable expression start
WIZZ-P028Expected closing parenthesis
WIZZ-P029Expected property after .
WIZZ-P030Unexpected expression token
WIZZ-P031Missing {#if} condition
WIZZ-P032Dynamic attribute missing expression
WIZZ-P033Template expression error
WIZZ-P034Invalid prop name
WIZZ-P035Reserved prop prefix (__wizz)
WIZZ-P036export let without a name
WIZZ-P037export not at top level
WIZZ-P038One prop per export let
WIZZ-P039Invalid prop declaration
WIZZ-P040Duplicate prop
WIZZ-P041Prop missing value
WIZZ-P042Prop missing semicolon
WIZZ-P043Unsupported export syntax
WIZZ-P044persist() on a prop
WIZZ-P045persist() on a const
WIZZ-P046persist() not at top level
WIZZ-P047Statements trailing a persist() declaration
WIZZ-P048persist() missing arguments
WIZZ-P049persist() extra arguments
WIZZ-P050persist() key not a string literal
WIZZ-P051Nested persist()
WIZZ-P052Unparseable persist() call
WIZZ-P053Expression depth budget exceeded
WIZZ-P054Template depth budget exceeded

Generate stage — WIZZ-G

CodeCondition
WIZZ-G001Template has no root element
WIZZ-G002{#each} outside a native element
WIZZ-G003{#each} body must have one root element
WIZZ-G004Component inside {#each}
WIZZ-G005Unsupported node inside {#each} (nested blocks)
WIZZ-G006on: directive inside {#each}
WIZZ-G007Invalid prop attribute name
WIZZ-G008Children on a component tag
WIZZ-G009Component missing its id (internal pipeline guard)
WIZZ-G010Component tag outside a native element
WIZZ-G011on: directive on a component tag
WIZZ-G012Event directive missing handler
WIZZ-G013Invalid event directive
WIZZ-G014Disallowed child inside <title>
WIZZ-G015Assignment to a prop
WIZZ-G016Imports must each be on their own line
WIZZ-G017Reserved reactive state name (__proto__, __wizz…)
WIZZ-G018Server build: unsupported node
WIZZ-G019Server build: unsupported head node
WIZZ-G020Server build: component not server-renderable
WIZZ-G021Void element with children
WIZZ-G022Hydration: unsupported node
WIZZ-G023Hydration: unsupported node in {#each}
WIZZ-G024persist() rewrite drift (internal guard)