- Notifications
You must be signed in to change notification settings - Fork48
WebReflection/uhtml
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Social Media Photo byAndrii Ganzevych onUnsplash
I'm on vacation! The fastest version and most battle tested version of this library isv4.
If you are usingv4, please keep doing that!
If you're happy to tryv5 please file issues in here, don't expect me to react out of tweets, and thank you for helping me out with use cases I couldn't even think about.
v5 is a rewrite from scratch based on another library (which isPython based) that works perfectly finebut it doesn't have a reactivity story fully attached yet.
This rewrite feels good, it avoids unnecessary loops, but it's also naively based onsignals for everything that was way easier to control before ...a whole render each time, never atomic, never considering edge cases around conditional arrays and what not.
I understand, now that signals are in, everyone is going to use signals for everything, as a distributed shared state of everything you are doing, but as a person that alaywas provided libraries to keep it simple, I couldn't even think about some of the scenarios you are "abusing" (no offence, my shortsighting) signals for, so my deepest apologies if the current state ofv5 cannot meet your expectations, I've tried my best, and unfortunately rushed a little bit, with this release, but all the ideas behind represent where I want to go from now on.
Again, apologies for not delivering like I've done before but be assured all the dots will be soon connected in a better way, or at least one that works reliably 👋
P.S.v4 is right here:https://github.com/WebReflection/uhtml/tree/v4
A minimalistic library to create fast and reactive Web pages.
<!doctype html><scripttype="module">import{html}from'https://esm.run/uhtml';document.body.prepend(html`<h1>Hello DOM !</h2>`);</script>
uhtml (microµ html) offers the following features without needing specialized tools:
- JSX inspired syntax through template literal
htmlandsvgtags - React like components withPreact likesignals
- compatible with native custom elements and other Web standards out of the box
- simplified accessibility via
ariaattribute and easydataset handling viadata - developers enhanced mode runtime debugging sessions
test incodepen
import{html,signal}from'https://esm.run/uhtml';functionCounter(){constcount=signal(0);returnhtml`<buttononClick=${()=>count.value++}> Clicked${count.value} times</button> `;}document.body.append(html`<${Counter}/>`);
If you are familiar withJSX you will finduhtml syntax very similar:
- self closing tags, such as
<p /> - self closing elements, such as
<custom-element>...</> - object spread operation via
<${Component} ...${{any: 'prop'}} /> keyattribute to ensuresame DOM node within a list of nodesrefattribute to retrieve the element via effects or by any other mean
The main difference betweenuhtml andJSX is thatfragments donot require<>...</> around:
// uhtml fragment examplehtml`<div>first element</div><p> ...</p><div>last element</div>`
On top ofJSX like features, there are other attributes with a special meaning:
ariaattribute to simplifya11y, such as<button aria=${{role: 'button', labelledBy: 'id'}} />dataattribute to simplifydataset handling, such as<div data=${{any: 'data'}} />@eventattribute for generic events handling, accepting an array whenoptions are meant to be passed, such as<button @click=${[event => {}, { once: true }]} />on...prefixed, case insensitive, direct events, such as<button onclick=${listener} />.directproperties access, such as<input .value=${content} />,<button .textContent=${value} />or<div .className=${value} />?toggleboolean attributes, such as<div ?hidden=${isHidden} />
All other attributes will be handled via standardsetAttribute orremoveAttribute when the passed value is eithernull orundefined.
Elements that containdata such as<script> or<style>, or those that contains text such as<textarea> requireexplicit closing tag to avoid having in between templates able to break the layout.
This is nothing new to learn, it's just how the Web works, so that one cannot have</script> within a<script> tag content and the same applies in here.
Indebugging mode, an error telling you which template is malformed will be triggered in these cases.
Useful for developers but never really relevant for end users,comments are ignored by default inuhtml except for those flagged as "very important".
The syntax to preserve a comment in the layout is<!--! important !-->. Every other comment will not be part of the rendered tree.
html`<!--! this is here to stay !--><!--// this will go --><!-- also this -->`
The result will be a clear<!-- this is here to stay --> comment in the layout without starting and closing!.
There are two kind of "logical comments" inuhtml, intended to help its own functionality:
<!--◦-->holes, used topin in the DOM tree where changes need to happen.<!--<>-->and<!--</>-->persistentfragments delimeters
Thehole type might disappear once replaced with different content while persistent fragments delimeters are needed to confine and/or retrieve back fragments' content.
Neither type will affect performance or change layout behavior.
import{// DOM manipulationrender,html,svg,unsafe,// Preact like signals, based on alien-signals librarysignal,computed,effect,untracked,batch,// extrasHole,fragment,}from'https://esm.run/uhtml';
In details
render(where:Element, what:Function|Hole|Node)to orchestrate one-off or repeated content rendering, providing a scopedeffect when afunction is passed along, such asrender(document.body, () => App(data)). This is the suggested way to enrich any element content with complex reactivity in it.htmlandsvgtemplate literal tags to create eitherHTML orSVG content.unsafe(content:string)to inject any content, evenHTML orSVG, anywhere within a node:<div>${unsafe('<em>value</em>')}</div>signal,computed,effect,untrackedandbatchutilities withPreact signals inspired API, fueled byalien-signalsHoleclass used internally to resolvehtmlandsvgtags' template and interpolations. This is exported mainly to simplifyTypeScript relaed signatures.fragment(content:string, svg?:boolean)extra utility, used internally to create eitherHTML orSVG elements from a string. This is merely a simplification of a manually created<template>element, itstemplate.innerHTML = contentoperation and retrieval of itstemplate.contentreference, use it if ever needed but remember it has no special meaning or logic attached, it's literally just standard DOM fragment creation out of a string.
The easiest way to start usinguhtml is viaCDN and here a few exported variants:
// implicit production versionimport{render,html}from'https://esm.run/uhtml';// https://cdn.jsdelivr.net/npm/uhtml/dist/prod/dom.js// explicit production versionimport{render,html}from'https://esm.run/uhtml/prod';// https://cdn.jsdelivr.net/npm/uhtml/dist/prod/dom.js// explicit developer/debugging versionimport{render,html}from'https://esm.run/uhtml/dev';import{render,html}from'https://esm.run/uhtml/debug';// https://cdn.jsdelivr.net/npm/uhtml/dist/dev/dom.js// automatic prod/dev version on ?dev or ?debugimport{render,html}from'https://esm.run/uhtml/auto';import{render,html}from'https://esm.run/uhtml/cdn';// https://cdn.jsdelivr.net/npm/uhtml/dist/prod/cdn.js
Usinghttps://esm.run/uhtml/cdn (or/auto) or the fully qualifiedhttps://cdn.jsdelivr.net/npm/uhtml/dist/prod/cdn.js URL provides an automatic switch todebug mode if the current page location contains?dev or?debug or?debug=1 query string parameter plus it guarantees the library will not be imported again if other scripts use a differentCDN that points at the same file in a different location.
This makes it easy to switch todev mode by changing the location fromhttps://example.com tohttps://example.com?debug.
Last, but not least, it is not recommended to bundle directlyuhtml in your project because components portability becomes compromised, as example, if each component bundles within itselfuhtml.
Another way to grantCDN and components portability is to use an import map and excludeuhtml from your bundler.
<!-- defined on each page --><scripttype="importmap">{"imports":{"uhtml":"https://cdn.jsdelivr.net/npm/uhtml/dist/prod/cdn.js"}}</script><!-- your library code --><scripttype="module">import{html}from'uhtml';document.body.append(html`Import Maps are Awesome!`);</script>
Minification is still recommended for production use cases and not only forJS, also for the templates and their content.
Therollup-plugin-minify-template-literals is a wonderful example of a plugin that does not complain aboutuhtml syntax and minifies to its bestuhtml templates in bothvite androllup.
This is arollup configuration example:
importterserfrom"@rollup/plugin-terser";importtemplateMinifierfrom"rollup-plugin-minify-template-literals";import{nodeResolve}from"@rollup/plugin-node-resolve";exportdefault{input:"src/your-component.js",plugins:[templateMinifier({options:{minifyOptions:{// allow only explicit <!--! comments !-->ignoreCustomComments:[/^!/],keepClosingSlash:true,caseSensitive:true,},},}),nodeResolve(),terser(),],output:{esModule:true,file:"dist/your-component.js",},};
The currentpareser is already environment agnostic, it runs on the client like it does in the server without needing dependencies at all.
However, the currentSSR story is still awork in progress but it's planned to land sooner than later.
About
A micro HTML/SVG render
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
