Fez → reactive components

directly in HTML with zero build steps

examplesplaygroundGitHub repo

<html> <head> <title>Time</title> <script script="/fez/core.js"></script> <script script="/fez/ui-time.fez"></script> </head> <body> <ui-time city="My place"></ui-time> <body> </html>

Fez brings a familiar component-based approach you know from Vue or React, but strips away the build complexity. It lets you write components naturally, right in your HTML, without needing a compilation step. Think of it as a jQuery for modern component development, with the simplicity of dropping a script tag into your page. You get to use your components as if they were native HTML elements, making the development experience straightforward and intuitive.

With reactive state and custom style.

  1. Add Fez JS (to HEAD)
  2. Create ex-counter component
  3. Place ex-counter anywhere in BODY
  4. That is it! Edit this demo on JSbin

You will learn all Fez features, just by inspecting these examples. You can edit and update both HTML and Fez code.

Demo: ui-avatar

  • features blocks {{#block ...}}
<style> .avatars { img.avatar { border: 2px solid #ddd; background-color: #fff; margin-right: -10px; } } </style> <div class="avatars"> <ui-avatar src="https://robohash.org/a.png" name="Dux"></ui-avatar> <ui-avatar src="https://robohash.org/b.png" name="Mile"></ui-avatar> <ui-avatar src="https://robohash.org/c.png?set=set2"></ui-avatar> <ui-avatar src="https://robohash.org/d.png?set=set2"></ui-avatar> <ui-avatar src="https://robohash.org/e.png" name="Joza"></ui-avatar> </div>
<template fez="ui-avatar"> <script> NAME = 'span' FAST = true connect(props) { this.copy('href', 'style') this.size = (props.size || 64) + 'px' this.$root.css({width: this.size, height: this.size}) } </script> <style> img { border-radius: 50%; } span.avatar { display: inline-block; text-align: center; .title { display: inline-block; border: 1px solid #ccc; background-color: #eee; padding: 1px 5px; font-size: 13px; position: relative; left: 3px; top: 8px; border-radius: 4px; } } </style> {{#block avatar}} <img src="{{ @props.src }}" class="avatar" style="width: {{ @size }}; height: {{ @size }};" /> {{/block}} {{#if @props.name}} <span class="avatar"> <div class="title">{{ @props.name }}</div> <div>{{#block:avatar}}</div> </span> {{:else}} {{#block:avatar}} {{/if}} </template>

Demo: ui-border

this will get border from fez component. Also check the DOM, fez parent is removed from DOM.
  • Features: internal method this.fezHide() will remove fez node from dom, and set this.root to first parent node and return initial child nodes.
  • useful if you need to transform children, but you can't have parent node in place. Think jQuery plugin.
<div class="should-be-first-parent"> <ui-border color="violet"> <div style="padding: 20px; max-width: 200px;"> this will get border from fez component. Also check the DOM, fez parent is removed from DOM. </div> </ui-border> </div>
<template fez="ui-border"> <script> onMount() { const childNodes = this.fezHide() $(childNodes).css('border', `3px solid ${this.props.color || 'black'}`) } </script> </template>

Demo: ui-card

The magician

Click to flip!

The high
pristess

Click to flip!

The empress

Click to flip!

Another clock?

Click to see

Any HTML or Fez component inside.
  • Simple UI component
<script> function flipAllCards() { let timeout = 150 let current = 0 $('.fez-ui-card').each((_, n) => { setTimeout(n.fez.flip, current += timeout) setTimeout(n.fez.flip, current + 1300) }) } </script> <button onclick="flipAllCards()">Flip all cards</button> <ui-card> <div class="front"> <h1>The magician</h1> <p>Click to flip!</p> </div> <div class="back" style="background: url('https://tarotatlas.com/wp-content/themes/ta/img/m1.webp') no-repeat center/cover;"> </div> </ui-card> <ui-card> <div class="front"> <h1>The high<br />pristess</h1> <p>Click to flip!</p> </div> <div class="back" style="background: url('https://tarotatlas.com/wp-content/themes/ta/img/m2.webp') no-repeat center/cover;"> </div> </ui-card> <ui-card> <div class="front"> <h1>The empress</h1> <p>Click to flip!</p> </div> <div class="back" style="background: url('https://tarotatlas.com/wp-content/themes/ta/img/m3.webp') no-repeat center/cover;"> </div> </ui-card> <ui-card> <div class="front"> <h1>Another clock?</h1> <p>Click to see</p> </div> <div class="back" style="background-color: #aba;"> <div style="width: 150px;"> <ui-clock></ui-clock> </div> <div style="padding: 30px;"> Any HTML or Fez component inside. </div> </div> </ui-card>
<template fez="ui-card"> <script> flip() { this.find('.card').classList.toggle('flipped') } autoflip() { setTimeout(()=>{ this.flip() this.autoflip() }, 5000 + (Math.random() * 15* 1000)) } connect() { this.autoflip() } </script> <style> .card { display: inline-block; float: left; width: 300px; height: 400px; perspective: 1000px; cursor: pointer; margin: 10px; } .fez-slot { position: relative; width: 100%; height: 100%; text-align: center; transition: transform 0.8s; transform-style: preserve-3d; } .card.flipped .fez-slot { transform: rotateY(180deg); } .front, .back { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; justify-content: center; align-items: center; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); } .front { background: #fff; color: #333; } .back { background: #fff; transform: rotateY(180deg); } .card:hover { transform: scale(1.02); transition: transform 0.3s ease; } </style> <div class="card" onclick="@flip()"> <slot /> </div> </template>

Demo: ui-clock

  • SVG generation
  • Features: reactive store - any update to this.state object triggers re-render.
  • Features: DOM morph updates - update only changed nodes and attributes
  • Features: dynamic styes (seconds line color)
  • Features: onDestroy() { ... } - execute code when component is removed from DOM
  • Featuers: setInterval() { ... } to set interval on a instance. No need to clear it, it will be auto cleared on component destroy.
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px;"> <div> <ui-clock city="Zageb" utc="1"></ui-clock> </div> <div> <ui-clock city="Moscow" utc="4"></ui-clock> </div> <div> <ui-clock city="NYC" utc="-5"></ui-clock> </div> <div> <ui-clock city="Mumbai" utc="5.5"></ui-clock> </div> <div> <ui-clock city="Sydney" utc="10"></ui-clock> </div> </div>
<template fez="ui-clock"> <script> randomColor() { const colors = ['red', 'green', 'blue', 'magenta', 'teal'] return colors[Math.floor(Math.random() * colors.length)]; } setVars() { // will only render once on next tick let time = new Date(Date.now() - (this.offsetHours * 60 * 60 * 1000)) this.state.hours = time.getHours() this.state.minutes = time.getMinutes() this.state.seconds = time.getSeconds() this.state.color = this.randomColor() } connect() { this.offsetHours = parseFloat(this.props.utc || 0) this.setVars() this.setInterval(this.setVars, 1000) } onDestroy() { console.log(`By from ${this.fezName} - ${this.props.city}`) } </script> <style> input { border: 3px solid red !important; } svg { width: 100%; height: 100%; } .clock-face { stroke: #333; fill: white; } .minor { stroke: #999; stroke-width: 0.5; } .major { stroke: #333; stroke-width: 1; } .hour { stroke: #333; stroke-width: 1.5; } .minute { stroke: #666; } .second, .second-counterweight { stroke: var(--color); } .second-counterweight { stroke-width: 3; } </style> <svg viewBox="-50 -50 100 100" style="--color: {{ @state.color }}"> <circle class="clock-face" r="48" /> <!-- markers --> {{#each [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55] as minute}} <line class="major" y1="35" y2="45" transform="rotate({{30 * minute}})" /> {{#each [1, 2, 3, 4] as offset}} <line class="minor" y1="42" y2="45" transform="rotate({{6 * (minute + offset)}})" /> {{/each}} {{/each}} <!-- hour hand --> <line class="hour" y1="2" y2="-20" transform="rotate({{30 * @state.hours + @state.minutes / 2}})" /> <!-- minute hand --> <line class="minute" y1="4" y2="-30" transform="rotate({{6 * @state.minutes + @state.seconds / 10}})" /> <!-- second hand --> <g transform="rotate({{6 * @state.seconds}})"> <line class="second" y1="10" y2="-38" /> <line class="second-counterweight" y1="10" y2="2" /> </g> <!-- City text --> {{#if @props.city }} <text x="0" y="-17" text-anchor="middle" dominant-baseline="middle" style="font-size: 10px; fill: #111;"> {{ @props.city }} </text> {{/if}} </svg> </template>

Demo: ui-editor

Hi
  • this is viewer / editor you see on the right
  • Features: Fez.head(...) that will safely insert script tags in the page header
  • Features: Fez.untilTrue(...) that will execute code every 100 miliseconds until it returns true
<ui-editor file="demo.html"> Hi </ui-editor>
<template fez="ui-editor"> <script> // you need to escape script tag inside fez script block window._fez_editor_assets = 0 Fez.head(` <link onload="_fez_editor_assets += 1" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css"> <s`+`cript onload="_fez_editor_assets += 1" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></sc`+`ript> `) class { reformatIndentation(text) { const lines = text.split('\n').filter(line => line.length > 0); if (lines.length < 2) return text; const secondLine = lines[1]; const baseIndent = secondLine.match(/^\s*/)[0].length; const targetIndent = Math.max(0, baseIndent - 2); const processedLines = lines.map(line => { const lineWithSpaces = line.replace(/\t/g, ' '); const leadingSpaces = lineWithSpaces.match(/^\s*/)[0].length; if (leadingSpaces === 0) return line; const newIndent = Math.max(0, leadingSpaces - targetIndent); return ' '.repeat(newIndent) + lineWithSpaces.trim(); }); return processedLines.join('\n'); } getSource() { return this.codeNode.textContent } copy() { navigator.clipboard.writeText(this.getSource()) Toast.info('File data copied to clipboard.') } connect(props) { props.language ||= 'html' const child = this.root.firstElementChild if (child?.nodeName == 'TEMPLATE' || child?.nodeName == 'XMP') { this.source = Fez.htmlEscape(this.root.firstElementChild.innerHTML) } else { this.source = this.root.innerHTML } } onMount() { const node = this.codeNode = this.find('code') node.innerHTML = this.reformatIndentation(this.source.trim()) Fez.untilTrue(()=>{ // hljs has a bug on applying styles on first load, this fixes it if (window.hljs && _fez_editor_assets > 1) { if (!node.classList.contains('hljs')) { hljs.highlightElement(this.codeNode) } else { return true } } }) } } </script> <style> code { padding: 10px; font-size: 15px; } pre { margin-top: 10px; width: 100%; } </style> {{#if @props.file}} <div style="font-size: 14px; margin: 0; position: relative; top: 7px;"> {{ @props.file }} ⋅ <button onclick="@copy()">Copy</button> {{#if @props.action }} ⋅ <button style="cursor: pointer; font-weight: 600;" onclick="@props.action('{{ @props.name }}')" >Update</button> {{/if}} </div> {{/if}} <pre> <code class="language-{{ @props.language }}" contenteditable="{{ !!@props.file }}"></code> </pre> </template>

Demo: ui-form





    
  • Featues: form helper - this.formData(). Get form data as object.
  • Featues: form helper - this.onSubmit(). If present, auto bind form and get form data as object
  • prefix params with : if you want to calculate attribute value. Same as in Vue, current example :ping="..."
  • Custom DOM tag name - FORM instead of default DIV
<div class="flex"> <ui-form target="/api" :ping="updateFormData"> <p> <input type="text" name="info" value="a dude" /> </p> <p> <select name="num"> <option>one</option> <option>two</option> <option>three</option> </select> </p> <p> <label><input type="radio" name="name" value="Jakov" /> Jakov</label> <label><input type="radio" name="name" value="Vid" /> Vid</label> <label><input type="radio" name="name" value="Dino" /> Dino</label> </p> <p> <button>Submit</button> </p> </ui-form> <ui-form target="/api" :ping="updateFormData"> <p> <select name="num"> <option>uno</option> <option>due</option> <option>tres</option> </select> </p> <p> <button>Submit</button> </p> </ui-form> </div> <pre id="form-data"></pre> <script> function updateFormData(obj) { $('#form-data').append(JSON.stringify(obj) + "\n") } </script>
<template fez="ui-form"> <script> NAME = 'form' // if you define onSubmit, you will get submited form data object // and event.preventDefault will allready be applied onSubmit(data) { this.props.ping(data) } </script> <style> border: 2px solid green; border-radius: 5px; padding: 15px; margin: 15px 0; background-color: #efe; label { display: block; cursor: pointer; margin-bottom: 5px; } select option { font-size: 16px; } </style> </template>

Demo: ui-icon



delete
  • Features: component to component communication
  • evalule value of a attribute if prefixed with column : (in this example :size=
<input type="range" min="24" max="100" class="slider" id="icon-range" oninput="Fez('#icon-blue').setSize(this.value)" /> ⋅ <span onclick="Fez('#icon-blue').attr('color', $(event.target).text())"> <button>red</button> <button>blue</button> <button>green</button> </span> <br /><br /> <ui-icon name="home"></ui-icon> <ui-icon id="icon-blue" name="settings" color="blue" onclick="alert(this.fez.props.color)" :size="document.getElementById('icon-range').value" ></ui-icon> <ui-icon color="red">delete</ui-icon>
<template fez="ui-icon"> <script> // everything before class gets executed in component registration $(document.head).append(` <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" /> `) class { NAME = 'span' setSize(size) { this.$root.css('font-size', `${parseInt(size)}px`) } onPropsChange(name, value) { if (name == 'color') { this.$root.css('color', value) } if (name == 'size') { this.setSize(value) } } connect(props) { this.copy('onclick') const icon = props.name || this.root.innerHTML.trim() this.color = props.color || '#00' this.root.classList.add('material-symbols-outlined') this.root.innerHTML = icon } } </script> <style> &.material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24 } </style> </template>

Demo: ui-slider

Side 1

Side 2

Side 3

Side 4

  • Add any HTML

Side 5

  • sweet component with css animations :)
  • Features: this.find() to find node in template, shortcut for this.root.querySelector()
  • Features: this.beforeRender() to prepare vars before render (clean alternative to svelte $: {...}
  • Features: this.onResize() to execute code on window resized. Auto cleared when node is removed.
<style> .fez-c-slider { .fez-slot { > div { text-align: center; } } } div.img { img { width: 100%; max-height: 600px; object-fit: cover; border-radius: 8px; } } </style> <ui-slider> <div> <h1>Side 1</h1> <div class="img"> <img src="https://images.unsplash.com/photo-1737143765999-bd3be790ab4f?w=600" /> </div> </div> <div> <h1>Side 2</h1> <div class="img flex"> <img src="https://images.unsplash.com/photo-1735767975829-71496633d499?w=600" /> <img src="https://images.unsplash.com/photo-1736158064402-5b68c2cbcc77?w=600" /> </div> <div style="width: 200px; margin: 20px auto;"> <ui-clock city="Slider!"></ui-clock> </div> </div> <div> <h1>Side 3</h1> <div class="img"> <img src="https://images.unsplash.com/photo-1737898415581-7dea57a1905b?w=600" /> </div> </div> <div> <h1>Side 4</h1> <div class="img"> <img src="https://images.unsplash.com/photo-1736158064402-5b68c2cbcc77?w=600" /> </div> <ul> <li>Add any HTML</li> </ul> </div> <div> <h1>Side 5</h1> <div class="img"> <img src="https://images.unsplash.com/photo-1736185669686-f302d6274f23?w=600" /> </div> </div> </ui-slider>
<template fez="ui-slider"> <script> cssVars() { return [ `--arrow-width: ${this.arrowWidth}px`, `--offset: -${this.state.offset}px`, ] } setSlide(num) { this.state.slide = num } changeSlide(direction){ this.state.slide += direction const slides = this.find('.fez-slot').querySelectorAll(":scope > div") if (this.state.slide < 0) { this.state.slide = slides.length - 1 } else if (!slides[this.state.slide]) { this.state.slide = 0 } } beforeRender() { const node = this.find('.slot') if (node) { this.state.offset = node.getBoundingClientRect().width * this.state.slide } } connect() { this.arrowWidth = 100 this.setSlide(0) this.onResize(this.render, 100) } </script> <style> table.slides { td.arrow { cursor: pointer; div { min-width: var(--arrow-width); display: flex; justify-content: center; align-items: center; font-size: 50px; color: #aaa; span { transform: rotate(-90deg); } } &:hover span { color: #111; } &:nth-child(3) { span { transform: rotate(90deg) translateY(10px); } } } td { div.slot { overflow: hidden; display: flex; align-items: flex-start; max-width: 100%; .slot-parent { transition: transform 0.3s ease; transform: translateX(var(--offset)); .fez-slot { display: flex; & > div { width: 100%; flex-shrink: 0; } } } } } } </style> <table class="slides" style="{{ @cssVars().join(';') }}"> <tr> <td class="arrow" onclick="@changeSlide(-1)"> <div> <span>⇧</span> </div> </td> <td> <div class="slot"> <div class="slot-parent"> <slot /> </div> </div> </td> <td class="arrow" onclick="@changeSlide(1)"> <div> <span>⇧</span> </div> </td> </tr> </table> </template>

Demo: ui-tabs

First tab


First tab

second tab

first tab

second tab


image tab

Third tab
  • Features: alternative node builder
  • nested and recursive components (tabs in tabs in tabs)
  • this.childNodes() FEZ instance helper function, get all first level child nodes, excluding #text nodes.
<ui-tabs> <div title="Bar"> <p>First tab</p> <br /> <ui-tabs> <div title="Foo nested 2">First tab</div> <div title="Bar"> <p>second tab</p> <ui-tabs> <div title="Foo nested 3">first tab</div> <div title="Bar nested 3"> <p>second tab</p> <hr /> <ui-clock></ui-clock> </div> </ui-tabs> </div> </ui-tabs> </div> <div title="Baz"> <h4>image tab</h4> <img src="./demo/fez.png" /> </div> <div title="Foo"> Third tab <hr /> <ui-clock></ui-clock> </div> </ui-tabs>
<template fez="ui-tabs"> <script> activateNode(node) { node.parent().find('> *').removeClass('active') node.addClass('active') } activate(num) { this.active = parseInt(num) const target = this.$root.find(`> div.header > span:nth-child(${num + 1})`) this.activateNode(target) this.activateNode(this.tabs[num]) } connect(props) { const { n, activate } = this; this.tabs = this.childNodes(n => $(n)) this.render([ n('div.header', this.tabs.map((tab, index) => n('span', tab.attr('title'), { onclick: ()=> activate(index) }) )), n('.body', '<slot />') ]); this.activate(0) } </script> <style> --tabs-border: 1px solid #ccc; max-width: calc(100%); .header { margin-bottom: -2px; position: relative; z-index: 1; & > span { border: var(--tabs-border); padding: 8px 15px; display: inline-block; border-radius: 8px 8px 0 0; margin-right: -1px; background: #eee; cursor: pointer; &.active { background-color: #fff; border-bottom: none; } } } .body { border: var(--tabs-border); padding: 8px 15px; background: #fff; & > div { display: none; &.active { display: block; } } } </style> </template>

Demo: ui-time

  • Slot state preservation demo:  
  • Features: slots - preserves original slot (fez tag innerHTML) on re-render
  • global and local css. If :fez { ... } is present, anything inside is local, anythig outside is global. If no class :fez is present, style is considered local.
<ui-time city="Zagreb"> <ul> <li> Slot state preservation demo:   <b class="color-name"></b> </li> </ul> </ui-time>
<template fez="ui-time"> <script> NAME = 'div' getRandomColor() { const colors = ['red', 'blue', 'green', 'teal', 'black', 'magenta', 'orange', 'lightblue'] return colors[Math.floor(Math.random() * colors.length)] } setRandomColor() { const color = this.getRandomColor() // this.find('.color-name').innerHTML = color this.root.querySelector('.color-name').innerHTML = color this.root.style.borderColor = color } getTime() { return (new Date()).getTime() } setTime() { this.val('.time', this.getTime()) } afterRender() { this.setTime() } connect() { this.setInterval(this.setTime, 1000) this.setRandomColor() } </script> <style> /* styles are applied to body, so this becomes body background color */ background-color: #f7f7f7; /* local component style applied to mounted component root */ :fez { border: 10px solid green; border-radius: 10px; padding: 10px; background-color: #fff; button { font-size: 16px; } } </style> <p>Param city: {{ @props.city }}</p> <p>Time now: <span class="time"></span></p> <p>Random num: <span>{{ Math.random() }}</span></p> <button onclick="@setRandomColor()">random color</button> ⋅ <button onclick="@render()">refresh & preserve slot</button> <hr /> <slot></slot> </template>

Demo: ui-toast

  • Features: class header that gets executed on component initializer.
<form onsubmit="showToast(this); return false"> <input type="text" name="toats" /> <button class="btn">Show info</button> </form> <script> function showToast(form) { let input = $(form).find('input') Toast.info(input.val()) input.val('') } </script>
<template fez="ui-toast"> <script> // helper function window.Toast = { info: (text) => { document.getElementById('ui-toast').fez.info(text) } } // auto init $(document.body).append('<ui-toast id="ui-toast"></ui-toast>') class { info(text) { const node = $('<div class="toast info">').html(text) $(this.find('.parent')).prepend(node) setTimeout(()=>{ node.addClass('leave') //node.remove() }, 3000) } } </script> <style> .parent { width: 300px; position: fixed; top: 20px; right: 20px; div.toast { padding: 10px 20px; background-color: #fff; margin-bottom: 20px; border-radius: 10px; box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; animation: dropIn 0.5s ease-out; &.leave { animation: dropOut 0.3s ease forwards; } &.info { border: 1px solid lch(50% 50 140);; background-color: lch(90% 50 140); } } } @keyframes dropIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } @keyframes dropOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(20px); } } </style> <div class="parent"></div> </template>

Demo: ui-todo

  • fully rective state
  • Features: fez-bind - two way binding of input element
  • Features: fez-use - when node is added to dom, call described function and pass node as reference (inspired by Svelte)
<ui-todo></ui-todo>
<template fez="ui-todo"> <script> clearCompleted() { this.state.tasks = this.state.tasks.filter((t) => !t.done) } removeTask(index) { this.state.tasks = this.state.tasks.filter((_, i) => i !== index); } addTask() { // no need to force update template, this is automatic because we are using reactiveStore() this.counter ||= 0 this.state.tasks.push({ name: `new task ${++this.counter}`, done: false, animate: true }) } animate(node) { // same as in Svelte, uf you define fez-use="methodName", method will be called when node is added to dom. // in this case, we animate show new node $(node) .css('display', 'block') .animate({height: '33px', opacity: 1}, 200, () => { delete this.state.tasks[this.state.tasks.length-1].animate $(node).css('height', 'auto') }) } connect() { this.state.tasks = [ {name: 'First task', done: false}, {name: 'Second task', done: false}, {name: 'Third task', done: true }, ] } </script> <h3>Tasks</h3> {{#if !@state.tasks[0] }} <p>No tasks found</p> {{/if}} {{#for task, index in @state.tasks}} {{#if task.animate}} <!-- this is fine because this is string templating --> <p fez-use="animate" style="display: none; height: 0px; opacity: 0;"> {{:else}} <p> {{/if}} <input type="text" fez-bind="state.tasks[{{index}}].name" style="{{ task.done ? 'background-color: #ccc;' : '' }}" /> ⋅ <input type="checkbox" fez-bind="state.tasks[{{index}}].done" /> ⋅ <button onclick="@removeTask({{ index }})">×</button> </p> {{/for}} <p> <button onclick="@addTask()">add task</button> ⋅ <button onclick="@clearCompleted()">clear completed</button> </p> <pre class="code">{{ JSON.stringify(this.state.tasks, null, 2) }}</pre> <p>If you want to preserve state in templates, wrap content in "fez-slot"</p> <p>Refresh: {{Math.random()}}</p> <p class="fez-slot"> Do not refresh: {{Math.random()}}. </p> </template>

Fez was created by @dux in 2024.