Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active February 6, 2024 15:50
Show Gist options
  • Save WebReflection/761052d6dae7c8207d2fcba7cdede295 to your computer and use it in GitHub Desktop.
Save WebReflection/761052d6dae7c8207d2fcba7cdede295 to your computer and use it in GitHub Desktop.
A recap of my FE / DOM related libraries

My FE/DOM Libraries

a gist to recap the current status, also available as library picker!

Minimalistic Libraries

do one thing only and do it well

  • µhtml (HTML/SVG auto-keyed and manual keyed render)
  • augmentor (hooks for anything)
  • wickedElements (custom elements without custom elements ... wait, what?)

Reactive Primitives (client/server)

  • µsignal (a tiny blend of solid-js and preact/signals)
  • µhooks (a React hooks alternative)

Enriched Libraries

compromise between small and features rich

Combo Libraries

integrated libraries

Minimalistic Combo

you choose what to use

  • wickedElements & µhtml or lighterhtml (easy)
  • hookedElements & µhtml or lighterhtml (even easier)
  • dom-augmentor & µhtml or lighterhtml (not easy at all, try µland or neverland instead)
  • native Custom Elements and µhtml or lighterhtml are an option too

DOM Engines Features Comparison

µhtml lighterhtml hyperHTML
released early 2020 late 2018 early 2017
browsers compatibility IE11+ & mobile IE9+ & mobile IE9+ & mobile
brotli min & transpiled 2.8K 4.8K 6.8K
brotli min & ecmascript 2.5K 4.2K not built
performance ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
simple/friendly API
sparse attributes
events attribute
events options
style special case opt-in: µstyler
aria special case since v3
ref special case
.dataset=${obj} helper
data & props setters
direct .setters=${...} since v2.32
boolean ?attr=${...} since v4.2 since v2.34
has own components too
user-land defined intents
auto keyed nodes
manually keyed nodes
Custom Elements compat.
Shadow DOM compat.
NodeJS SSR compat. µhtml-ssr / µcontent heresy-ssr viperHTML
hooks friendly
<self-closing-tags />
attributes callbacks ref only
content callbacks
promises as content
promises placeholders
dis/connected events only version plus
automatic fragments
interpolated HTML content
HTML injection safety
Companions & wrappers 1, 2, 3, 4, 5, 11 1, 4, 5, 6, 7, 8 1, 9
Features Score 30 33.5 26.5

About HTML injection safety

In hyperHTML an array of strings, or primitives, used as interpolation is automatically injected as HTML. This was an early design/feature mistake, non existent in other libraries.

Both lighterhtml and hyperHTML accepts an explicit {html: ...} interpolation when injecting HTML is meant, hence not accidental, while uhtml can either use ref=callback(node) or use html([txt]) instead of a template literal to parse [txt] as if it was a template literal, but it's parsed each time if the array is different each time, hence discouraged, yet possible.

Companions & wrappers

  1. vanilla JS
  2. µce / µce-template
  3. µhtml-ssr / µcontent
  4. wickedElements
  5. hookedElements
  6. neverland
  7. heresy
  8. heresy-ssr
  9. HyperHTMLElement
  10. µland

DOM Engines Summary

In older to newer library order

  • hyperHTML: early 2017, first of a kind, early design, battle tested
  • lighterhtml: late 2018, much better DX and easier to integrate than hyperHTML
  • µhtml: early 2020, it's an essential subset of lighterhtml

hyperHTML Pros and Cons

  • shipped to millions since 2018, enterprise grade
  • today is best used via its HyperHTMLElement helper
  • it included too many features that are rarely used
  • it's features complete, mostly maintenance only
  • it has a viperHTML SSR counter part, but it's been deprecated

lighterhtml Pros and Cons

  • smaller than hyperHTML but even if it shares most of its core, it's faster in most cases
  • it has a superior DX compared to hyperHTML
  • almost every internal behavior can be customized
  • it plays better with hooks and ref=${...} pattern

µhtml Pros and Cons

  • it's an essential lighterhtml subset, for half of the size
  • you can start small, and switch to lighterhtml without touching code
  • the only cons is that is a subset with its own constrains and/or limited features
- - -

F.A.Q.

Isn't all this hard to maintain?

Pretty much all my libraries share the same code behind the scene. The difference is in the user-facing API and the pattern provided by such API (hooks vs Custom Elements vs just render, etc).

If you find a bug in heresy, as example, and it comes from augmentor, everything else hooks based will be patched automatically.

Same goes for lighterhtml and hyperHTML, sharing domdiff module and much more: if I fix something there it'll propagates everywhere else.

uhtml is a little exception to the latter case but ... well, it's tiny indeed, so it's the easiest to patch/maintain, specially 'cause it's code is mostly a copy/paste from lighterhtml, so that if I fix one, the other would pseudo-automatically follow.

What does auto-keyed mean?

All render engines avoid DOM trashes as much as they can. There are basically two ways to do this: use an index, automatically recycling the same node with such index, or use an explicit reference, manually coupling a specific part of the stack to such reference.

µhtml and lighterhtml support both approaches: in the former case, each node keeps being updated with new info, if found at the same position it was before, and only if the the new info changed, keeping data and UI strictly decoupled.

On the other hand, all rendering engines allows explicit references: same reference gets same node/stack each time, and DOM nodes are moved around whenever the reference is found in another position (i.e. list of items).

In the µhtml and lighterhtml case, the index, auto-keyed, approach, is the default, but you can always use html.for(...) or svg.for(...) to switch to the keyed pattern, following an example:

// auto-keyed lists example
render(viewNode, html`
  <ul>
    ${items.map(item => html`<li>${item.text}</li>`)}
  </ul>
`);

// manually keyed lists example
render(viewNode, html`
  <ul>
    ${items.map(
      item => html.for(item)`<li>${item.text}</li>`
    )}
  </ul>
`);

However, if the list of items have a unique id, you can always use <li data-id=${item-id}> in case you need to retrieve the associated data later on.

@msand
Copy link

msand commented Feb 29, 2020

Thanks for the summary @WebReflection fascinating to follow your work. I'm wondering, what it would take for me to integrate react-native-svg, or make it compatible with nativeHTML, perhaps that could increase interest in it? Been thinking about making it compatible with flutter as well, and normal native mobile development. Was intending to look into nativeHTML some time ago, but work and other priorities haven't made that feasible yet. Would you be able to compare it to the architecture of react-native / ViewManagers? Perhaps it can help me make a proof of concept faster.

@WebReflection
Copy link
Author

@msand as pity as it sounds, I can't, and don't want to, compete with React native. It's like ... one person against Spartans.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment