# \*mizu
## Phase 1 — ELIGIBILITY
Enables *mizu.js* rendering for the element and its children.
```html
<main *mizu> <!--...--> </main>This directive must not have any [tag] or .modifiers. If it does, the directive will be ignored. You can choose whether to require this directive for mizu.js rendering with the implicit option when using the user API. By default, rendering is explicit in Client-Side APIs and implicit in Server-Side APIs.
The closest element that declares a *mizu directive.
Sets context values for an element and its children.
<div *set="{ foo: 'bar' }"> <!--<span *text="foo"></span>--> </div>The context must resolve to a JavaScript Object. The context is initialized once and persists across renderings, but it can still be updated by other directives.
Creates a reference to an element for later use.
<div *ref="foo" data-text="bar"> <!--<p *text="$refs.foo.dataset.text"></p>--> </div>Redefining a reference will shadow its previous value within the current subtree, without affecting its value in the parent subtree.
A collection of all referenced elements within the current subtree.
Skip expression evaluation if set.
Conditionally renders an element.
<div *if="true"> <!--...--> </div>There is currently no special handling for <template> elements, but future versions may introduce specific behavior for these elements.
Conditionally renders an element placed after another *if or *else directive.
<div *if="false"></div> <div *else="false"></div> <div *else><!--...--></div>Must be placed immediately after an element with an *if or *else directive. There is currently no special handling for <template> elements, but future versions may introduce specific behavior for these elements.
Conditionally displays an element.
<div *show="true"> <!--...--> </div>When hidden, the element's CSS display property is set to none !important. When shown and if initially hidden by a CSS stylesheet (display: none), the element's display property is reset to initial !important. Unlike *if and *else directives, the element remains in the DOM when hidden. You can take advantage of the default value being true to hide elements before mizu.js loads (e.g. <style>[*show]{display:none}</style>).
Renders an element for each iteration performed.
<!--<ul>--> <li *for="let item of items"></li> <!--</ul>-->The expression can be:
- Any syntax supported inside
for,for...inandfor...ofloops. - Any iterable object that implements
Symbol.iterator.- The iterated key is exposed as
$key. - The iterated value is exposed as
$value.
- The iterated key is exposed as
- A finite
number.- The directive is applied the specified number of times.
There is currently no distinction between let, const and var declarations inside for loops, but future versions may introduce specific behavior for these. There is currently no special handling for <template> elements, but future versions may introduce specific behavior for these elements.
The evaluated value of the *id directive if present, or the auto-generated identifier.
The total number of iterations.
The current iteration index (0-based).
The current iteration index (1-based, same as $i + 1).
Whether this is the first iteration (same as $i === 0).
Whether this is the last iteration (same as $i === ($iterations - 1))
Hint for *for directive to differentiate generated elements.
<!--<ol>--> <li *for="const {id} of items" *id="id"></li> <!--</ol>-->Must be used on an element with a *for directive. Identifiers must be unique within the loop, any duplicates will be replaced by the last occurrence.
Conditionally render an element after a *for directive.
<article *for="const article of articles"></article> <p *empty.not *text="`${$generated} results`"></p> <p *empty><!-- No results.--></p>Must be placed immediately after an element with a *for or another *empty directive. Elements generated by the *for directive do not apply to this restriction. There is currently no special handling for <template> elements, but future versions may introduce specific behavior for these elements.
The number of elements generated by the preceding *for directive. This value may differ from the actual number of iterations processed if conditional directives were applied.
Inverts the condition, rendering the element when at least one element is generated.
Sets element's textContent.
<p *text="'...'"> <!--...--> </p>HTML content is automatically escaped. Without an attribute value, this directive escapes the element's innerHTML (e.g., <a *text><b></b></a> becomes <a *text><b></b></a>).
Sets element's innerHTML.
<template *html="'<p>...</p>'"> <!--<p>...</p>--> </template>Raw HTML can introduce XSS vulnerabilities. Exercise caution when using expressions from untrusted sources.
Enables content interpolation within mustaches ({{ and }}) from Text child nodes.
<p *mustache> <!--{{ ... }}--> </p>Interpolation occurs only within Text nodes, not the entire element. HTML content is automatically escaped. There is currently no distinction between double mustaches ({{ and }}) and triple mustaches ({{{ and }}}), but future versions may introduce specific behavior for these.
Sets element's innerHTML after performing syntax highlighting.
<code *code[ts]="'...'"> <!--<span class="hljs-*">...</span>--> </code>This directive dynamically imports highlight.js. Unsupported languages default to plaintext.
Any supported language identifier or alias.
Remove leading/trailing whitespaces and shared indentation.
Sets element's innerHTML after performing markdown rendering.
<div *markdown="'*...*'"> <!--<em>...</em>--> </div>This directive dynamically imports @libs/markdown.
Load additional Markdown plugins by specifying a comma-separated list (e.g., *markdown[emojis,highlighting,sanitize] ). See the full list of supported plugins at @libs/markdown/plugins. Unsupported plugins will be silently ignored.
Create a table of contents from <h1>...<h6> elements found in selected target.
<nav *toc="'main'"> <!--<ul>...</ul>--> </nav>Heading elements must meet these criteria:
- Include an
idattribute. - Contain an immediate
<a>child with an anchor link pointing to its parentid. - Follow a descending order without skipping levels.
When a heading is found, the next level headings are searched within its parentElement. If the parent is an <hgroup> or has a *toc[ignore] attribute, the search moves to the grandparent element.
Define which heading levels to include:
- Specify a single level (e.g., *toc[h2]).
- Add a
+to include higher levels (e.g., *toc[h2+]).
- Add a
- Use a range with a
-to specify multiple levels (e.g., *toc[h2-h4]). - Use
ignoreto exclude an element from traversal (e.g., *toc[ignore]). No other modifiers or attribute value should be used with this.
Cleans up the element and its children from specified content.
<div *clean> <!--...--> </div>Remove all Comment nodes within the subtree.
Remove all spaces (except non-breaking spaces ) within the subtree.
Clear all <template> nodes from the subtree after fully processing it.
Strip all known directives from the subtree after fully processing it. If the .comments modifier is also enabled, comments generated by directives will be removed as well.
Registers a new custom element.
<template *custom-element="my-element"> <ul><slot name="items"></slot></ul> </template>Must be defined on a <template> element. The tagname must be a valid custom element name. Valid custom element names may be specified as is. Custom elements registered this way do not use Shadow DOM, their content is rendered directly.
A record of #slot elements by <slot> name. The unnamed slot is accessible using $slots[""].
A record of HTML attributes specified on the custom element.
Replace occurrences of this custom element with its content. Note that $slots and $attrs variables are not accessible when using this modifier.
Specify target <slot> in an element defined by a *custom-element directive.
<my-element> <li #items><!--...---></li> </my-element>Elements without a #slot directive are appended to the default (unnamed) slot. Elements targeting the same slot are appended in the order they are defined.
Set an element tagname.
<div *is="'section'"> <!--...--> </div>If the tagname changes, the reference will also change. Equality checks with elements using this directive may not work as expected. Some directives may be incompatible with this directive.
Listens for a dispatched Event.
<button @click="this.value = 'Clicked!'"> <!--Not clicked yet.--> </button>Attach multiple listeners in a single directive using the shorthand @="object" (e.g., @="{ foo() {}, bar() {} }").
- Modifiers apply to all listeners in the directive (e.g., @.prevent="{}").
- Use tags to attach listeners with different modifiers (e.g., @[1]="{}" @[1].prevent="{}").
- HTML attributes are case-insensitive, so this is the only way to listen for events with uppercase letters or illegal attribute characters (e.g., @="{ FooBar() {}, Foobar() {} }").
To listen for events with dots . in their names, use brackets { } (e.g. @{my.event}).
(in listener only) The dispatched Event.
Optional tag to attach multiple listeners to the same event (e.g., @click[1], @click[2], etc.).
Call event.preventDefault() when triggered.
Call event.stopPropagation() when triggered.
Register listener with { once: true }.
Register listener with { passive: true }.
Register listener with { capture: true }.
Trigger listener only if event.target is the element itself.
Attach listener to a different target (e.g., window or document).
Prevent listener from being called more than once during the specified time frame.
Delay listener execution until the specified time frame has passed without any activity.
Specify which keys must be pressed for the listener to trigger on a KeyboardEvent.
- The syntax for keys constraints is defined as follows:
- Combine keys with a plus sign
+(e.g., @keypress.keys[ctrl+space]). - Separate multiple combinations with a comma
,(e.g., @keypress.keys[ctrl+space,shift+space]).
- Combine keys with a plus sign
- Supported keys and aliases:
altfor"Alt".ctrlfor"Control".shiftfor"Shift".metafor"Meta".spacefor" ".keyfor any key except"Alt","Control","Shift", and"Meta".- Any value returned by
event.key.
Binds an element's attribute value.
<a :href="url"> <!--...--> </a>Binding directives is not officially supported and is considered undefined behaviour. :class and :style have specific handling described below. Bind multiple attributes in a single directive using the shorthand :="object" (e.g. :="{ foo: 'bar', bar: true }"). The directive value may be omitted to bind an attribute to an identifier with the same name (after camelCase conversion if the attribute contains hyphens - ) in the current context. (e.g., :data-foo is equivalent to :data-foo="dataFoo"). Boolean attributes defined by the HTML spec are handled accordingly (removed when falsy). Attributes with null or undefined values are removed.
Binds an element's class attribute.
<p :class="{ foo: true, bar: false }"> <!--...--> </p>The expression can be:
- A
stringof space-separated class names (e.g., "foo bar"). - A
Record<PropertyKey, boolean>mapping class names to their state (e.g., { foo: true, bar: false }). - An
Arrayof the supported types (e.g., [ "foo", { bar: false }, [] ])
The initial class attribute value is preserved. Class names with at least one truthy value are treated as active.
Binds an element's style attribute.
<p :style="{ color: 'salmon' }"> <!--...--> </p>The expression can be:
- A
stringsupported byHTMLElement.style.cssText(e.g., "color: blue;"). - A
Record<PropertyKey, unknown>mapping CSS properties to their values (e.g., { backgroundColor: "red", "border-color": "green", width: 1 }).- Use camelCase instead of kebab-case to avoid escaping CSS property names.
- Values of type
numberare implicitly converted topxunits when applicable (HTMLElement.style.setProperty() will be called with "px" appended).
- An
Arrayof the supported types (e.g., [ "color: blue", { backgroundColor: "red" }, [] ])
The initial style attribute value is preserved. CSS properties are processed in the order they are defined, regardless of !important.
Binds an <input>, <select> or <textarea> element's value attribute in a bi-directional manner.
<select ::value="foo"> <!--<option>...</option>--> </select><input type="checkbox"> and <select multiple> elements will bind to an array of values. Using a modeled value within @input or @change expressions can cause precedence issues, as the model relies on these events to update. To avoid this, listen to the :: event, which is always triggered after the model has been updated. Must be used on elements with a value property, such as <input>, <select>, or <textarea>. For other elements, use the :attribute directive. The .nullish, .boolean, .number, and .string modifiers are currently implemented as boolean modifiers, but future versions may change this behavior to offer more parsing features. You can use the shorthand syntax ::="model" instead of ::value="model".
Change the Event that triggers the model update. Recommended events are "input" or "change".
Automatically set the input name attribute based on the attribute's value (e.g., <input ::.name="foo"> becomes <input name="foo">). The default is true for <input type="radio"> and <input type="checkbox">, and false for all other elements.
Initialize the model using the nullish coalescing operator and the input value attribute if present (e.g., <input ::.value="foo" value="bar"> assigns foo the value "bar" if it was nullish).
Prevent the listener from being called more than once during the specified time frame.
Delay listener execution until the specified time frame has passed without any activity.
Specify which keys must be pressed for the listener to trigger on a KeyboardEvent. See @event.keys modifier for more information.
Set the model value to null if the value is empty.
Convert the model value using Boolean(). Additionally, any non-empty value matching the YAML 1.1 definition of falsy boolean values are set to false.
Convert the model value using Number().
Convert the model value using String().
Performs a fetch() call that can be handled by %response directives.
<div %http="https://example.com"> <!--...--> </div>Without a %response directive, the request won't be performed automatically. Use %response.void if you want to trigger the request but ignore the response. Valid URLs may be specified as is. A new request is triggered for the same element if:
- Its reference changes.
- The evaluated URL changes.
Since predicting when a new request will be performed is challenging, use this directive only for read-only operations. For endpoints with side effects, consider the %@event directive.
(in url expression only) The dispatched Event if triggered by a %@event directive, or null.
Control whether fetch() should follow redirections.
Whether to update the browser history with history.pushState() for the target URL (must be the same origin).
Set the HTTP method (the value is uppercased). This modifier should not be used with its aliases.
Alias for .method[get].
Alias for .method[head]
Alias for .method[post]
Alias for .method[put]
Alias for .method[patch]
Alias for .method[delete]
Set HTTP headers for a %http directive.
<div %header[x-foo]="'bar'"> <!--...--> </div>Headers with undefined or null values are deleted. Headers with Array values are appended together.
Header name.
Set HTTP body for a %http directive.
<div %body.json="{foo:'bar'}"> <!--...--> </div>A Headers object containing all registered headers from %header directives attached to the element.
Format the body with the specified type:
text: format body withtoString().form: format body withURLSearchParams.json: format body withJSON.stringify().xml: format body withstringify()from @libs/xml/stringify. Using this value will dynamically import @libs/xml.
This modifier should not be used with one of its aliases.
Automatically set the Content-Type header when using a .type modifier:
text: setContent-Type: text/plain.form: setContent-Type: application/json.json: setContent-Type: application/x-www-form-urlencoded.xml: setContent-Type: application/xml.
If the header was already set, it is overwritten.
Alias for .type[text].
Alias for .type[form].
Alias for .type[json].
Alias for .type[xml].
Reacts to a %http directive's Response.
<div %http="'https://example.com'" %response.html> <!--...--> </div>A Response object containing the fetched data.
Contains the response.body (type depends on the modifier used).
Specify which HTTP status codes trigger this directive:
- The syntax for status code constraints is defined as follows:
- Define a range using a minus sign
-between two numbers (e.g., %response[200-299]). - Specify multiple ranges and statuses by separating them with a comma
,(e.g., %response[200,201-204]).
- Define a range using a minus sign
- Supported aliases:
2XXfor200-299.3XXfor300-399.4XXfor400-499.5XXfor500-599.
Consume the response.body:
void: discard body usingresponse.body?.cancel().text: consume body usingresponse.text()and set element'stextContentif no expression is provided.html: consume body usingresponse.text(), parse it into a<body>element, and set element'sinnerHTMLif no expression is provided.json: consume body usingresponse.json().xml: consume body usingresponse.text()and parse it withparsefrom @libs/xml/parse. Using this value will dynamically import @libs/xml.
This modifier should not be used with one of its aliases.
Alias for .consume[void].
Alias for .consume[text].
Alias for .consume[html].
Alias for .consume[json].
Alias for .consume[xml].
Consume body using response.text() and set target's outerHTML. This modifier takes precedence over the .consume modifier and makes it effectless, although if .consume[text] is set, swapped content will be escaped.
Any non-directive HTML attributes on the target will be applied to the swapped content elements.
Listens for a dispatched Event and re-evaluates %http directive before reacting to its Response.
<button %http="https://example.com" %@click.html> <!--...--> </button>Must be defined on an element that also possess a %http directive. This is essentially a combination of %response and @event directives. Target URL is still set by %http directive. As it is re-evaluated, you can however use the $event value to dynamically compute the target URL (e.g.%http="$event ? '/foo' : '/bar'"). All modifiers from %http directive are inherited, along with the RequestInit prepared by %header and %body directives.
(in listener only) The dispatched Event.
A Response object that contains the fetched data.
A variable that contains the response.body (typing depends on which modifier is used).
Inherited from @event and %response directives. See their respective documentation for more information.
Render an element once and skip subsequent updates.
Reprocess an element at a specified interval (in seconds).
<div *refresh="1.5"> <!--<time *text="new Date()"></time>--> </div>Ensure proper context management to prevent unexpected errors. Avoid using with iterative directives like *for as *refresh will be duplicated for each generated element. The target element will be rendered regardless of detected changes. This is useful for updating content that cannot be directly observed, but use sparingly to avoid performance issues. Set the interval to null to stop refreshing. If the element is commented out by a directive, the refresh is automatically cleared. Refresh operations are performed using setTimeout. New calls are scheduled when the directive is processed again, ensuring a consistent interval.
Indicates if the element is currently being refreshed.
Evaluate a JavaScript expression in the context of the element.
<div *eval="console.log('$data')"> <!--...--> </div>Use this directive sparingly, prefer alternative directives for better maintainability and security. This directive is intended for edge cases. The expression runs after the element and all its children have been fully processed.
Prevent an element from being processed.
<div *skip> <!--<p *text="foo"></p>--> </div>Special directive for testing purposes.
<samp ~test[testing].text="'...'"> <!--...--> </samp>For testing only. Use this directive to isolate and test custom directives without relying on others. The modifiers may not be compatible with each other.
Specify any existing Phase name (e.g., ~test[testing], defaults to Phase.TESTING). The directive will execute during the specified phase before any other directive in that phase, allowing you to simulate specific scenarios.
Set the element's textContent with the expression result.
Evaluate a JavaScript expression within the element's context.
Convert the element to a Comment if the expression is truthy, and revert it otherwise.
Throw an EvalError if the expression is truthy.
This directive does nothing.
<div *noop></div>Set up mizu.js in your browser environment using one of two methods:
- Immediately Invoked Function Expression (IIFE)
- EcmaScript Module (ESM)
On the client-side...
- Rendering is explicit, requiring the
*mizuattribute to enable mizu.js on a subtree. - Reactivity is enabled, so changes to contexts will trigger a re-render.
This setup automatically starts rendering the page once the script is loaded. It's the simplest way to get started but limited to the default configuration.
<script src="https://mizu.sh/client.js" defer></script>This setup requires you to import and start mizu.js manually, allowing customization of the rendering process, such as setting the initial context and loading additional directives.
<script type="module"> import Mizu from "https://mizu.sh/client.mjs" await Mizu.render(document.body, { context: { foo: "🌊 Yaa, mizu!" } })</script> Looking to effortlessly theme your new web page? Check out matcha.css!
<link rel="stylesheet" href="https://matcha.mizu.sh/matcha.css">To set up mizu.js in a server environment, install it locally. mizu.js packages are hosted on jsr.io/@mizu.
On the server side...
- Rendering is implicit, so the
*mizuattribute is not required. - Reactivity is disabled, meaning changes to contexts are not tracked and will not trigger a re-render.
Deno supports the jsr: specifier natively, allowing you to import mizu.js directly.
import Mizu from "jsr:@mizu/render/server" await Mizu.render(`<div *text="foo"></div>`, { context: { foo: "🌊 Yaa, mizu!" } })Alternatively, add it to your project using the Deno CLI.
deno add jsr:@mizu/renderAdd mizu.js to your project using the JSR npm compatibility layer.
# NodeJS npx jsr add @mizu/render# Bun bunx jsr add @mizu/renderOnce installed, use it in your project.
import Mizu from "@mizu/render/server" await Mizu.render(`<div *text="foo"></div>`, { context: { foo: "🌊 Yaa, mizu!" } })A HTML attribute recognized by mizu.js which instructs how it should process the element.
The syntax is as follows:
<tag *name [ tag ]. modifier [ value ] =" expression " />
Directives names often begin with special characters to prevent conflicts with standard HTML attributes and to clearly indicate their specific purpose:
*for generic directives.#for directives targeting<slot>elements.@for directives related toEventhandling.:for binding HTML attributes.::for bi-directional binding.
%for HTTP directives.%@for combined HTTP and Event directives.
~for testing directives.
Typically, this serves as the directive's argument. At most one tag can be specified per directive.
Modifiers adjust the behavior of the directive. You can specify multiple modifiers on the same directive.
Repeating a modifier on a single directive is considered undefined behavior.
The value of the modifier. The modifier may have an explicit