Awesome
Fluid 💧
Fluid is an user interface library for the web.
- Simple: Tiny API with only two functions,
html
andrender
- Lightweight: Under 2 KB minified and gzipped
- Friendly: No compilation or build step needed, just import and run
- Blazing Fast: Updates only what has changed without virtual DOM
- Flexible: No assumptions about anything, just a simple library
- Declarative: Just render everything when changes occur, no painful updates
Installation
Install Fluid directly from NPM with npm install @hereiskevin/fluid
or just try it out on CodeSandbox. Fluid is only distributed as an ECMAScript module.
If you're feeling ambitions, you can build Fluid from source.
$ git clone https://github.com/HereIsKevin/fluid.git
$ npm install
$ npm run build
That's it. You should now find your build in the dist
folder.
API
Fluid has a tiny API with only two exported functions, html
and render
.
html(strings, ...values)
Create a new template for Fluid. Once rendered, the types of each of the interpolated values may not change. Note that this caveat applies to all templates with identical strings.
Arguments
strings: TemplateStringsArray
: Static strings in templatevalues: unknown[]
: Dynamically changing values
Returns
Template
: Generated template
Example
const inner = (hidden, style) =>
html`
<div hidden?="${hidden}" style="${style}">
By adding "?" to the end of an attribute, it is toggled whenever its value
is true. Attributes can be interpolated with "${value}". Style attributes
can take an object of properties instead, which will result in style
diffing. Note that switching between objects and strings for style
attributes will result in unexpected behavior.
</div>
<div ref="${(node) => setValue("paragraph", node)}">
Reference attributes named "ref" can be used to get references to actual
nodes. The function passed as the value for the attribute will be called
when needed with the node passed as an argument.
</div>
<div>
<input .value="${value}" />
By adding "." to the beginning of an attribute, the property on the
element will be set to the passed value.
</div>
`;
const template = (inner) =>
html`
${inner}
<div @click="${() => alert("Event!")}">
By adding "@" to the beginning of an attribute, its value is set to be the
event handler for the event name in the attribute. Strings or templates
can be interpolated within elements. Strings are interpolated as text
nodes. Templates are directly inserted and are removed when their static
strings change.
</div>
${values.map((value) => inner(value.hidden, value.style))}
<div>
Arrays of templates can also be interpolated. When rendered, they will be
rendered as non-keyed, meaning that Fluid is free to reuse and move
templates around. This is not suggested for stateful elements like input
elements. Most of the time, this is going to be faster, but it will be
slower for removals or insertions in between existing templates.
</div>
${values.map((value) => [value.id, inner(value.hidden, value.style)])}
<div>
Arrays of keys with templates can also be interpolated. When rendered,
they will be rendered as keyed, meaning that Fluid will reorder and move
elements based on their keys. This is suggested for stateful elements like
input. This will be faster for removals and insertions in between existing
templates and slightly slower for most other things.
</div>
`;
render(target, template)
Render and update templates created by Fluid to the target.
Arguments
target: Element
: Target for renderingtemplate: Template
: Template to be rendered
Example
let hidden = false;
let style = "color: red";
const target = document.getElementById("view");
const update = () => render(target, template(inner(hidden, style)));
update();
Template
Templates are generated by html
. All exposed APIs on templates are subject to change and should not be used. Templates are considered "private" and are not truly part of the public API.
How It Works
- When
html
is called, it creates aTemplate
, which stores all the parameters without doing anything. This makes repetitive rendering calls very cheap. - When a
Template
object is passed torender
,render
will attempt to find an already compiled template. If it cannot, then it will compile the template. - Compiling templates involves using comment markers for interpolated values, then creating base updaters for each possible change.
- If the template compiler can find an already-compiled template, it makes a clone of it and binds the updaters. Otherwise, it compiles the template first.
render
then calls the updaters to insert or update the compiled templates. Since it already knows where all the changes will occur, updates are fast and inexpensive, unlike virtual DOM, which needs to rebuild and diff the whole tree every render.
Benchmarks
Fluid is the fastest library of its kind, even faster than lighterhtml and lit-HTML. For the source of the benchmarks, see my fork of js-framework-benchmark.
Non-Keyed
<p align="center"> <img src="./benchmarks/non_keyed.png" alt="Fluid Non-Keyed Benchmarks" width="710px" /> </p>Keyed
<p align="center"> <img src="./benchmarks/keyed.png" alt="Fluid Keyed Benchmarks" width="710px" /> </p>Support
Fluid is made by one developer, me (@HereIsKevin). Creating a high quality framework is time-consuming. If you find Fluid helpful, please give it a ⭐️ to show your support.