Skip to content

luwes/sinuous

Repository files navigation

Sinuous

Version Badge size codecov

npm:npm i sinuous
cdn:https://cdn.jsdelivr.net/npm/sinuous/+esm


  • Small.hello world at~1.4kBgzip.
  • Fast.top rankedof 80+ UI libs.
  • Truly reactive.automatically derived from the app state.
  • DevEx.no compile step needed, choose yourview syntax.

Add-ons

Size Name Description
Badge size sinuous/observable Tiny observable(included by default)
Badge size sinuous/map Fast list renderer
Badge size sinuous/hydrate Hydrate static HTML
Badge size sinuous/template Pre-rendered Template

Community

Examples


Seecomplete docs,or in a nutshell...

View syntax

A goal Sinuous strives for is to have good interoperability. Sinuous creates DOM elements viahyperscripthcalls. This allows the developer more freedom in the choice of the view syntax.

Hyperscriptdirectly callh(type: string, props: object,...children).

Tagged templatestransform the HTML tohcalls at runtime w/ thehtml``tag or, at build time withsinuous/babel-plugin-htm.

JSXneeds to be transformed at build time first withbabel-plugin-transform-jsx-to-htmand after withsinuous/babel-plugin-htm.


Counter Example (1.4kB gzip) (Codesandbox)

Tagged template (recommended)

import{observable,html}from'sinuous';

constcounter=observable(0);
constview=()=>html`<div>Counter${counter}</div>`;

document.body.append(view());
setInterval(()=>counter(counter()+1),1000);

JSX

import{h,observable}from'sinuous';

constcounter=observable(0);
constview=()=><div>Counter{counter}</div>;

document.body.append(view());
setInterval(()=>counter(counter()+1),1000);

Hyperscript

import{h,observable}from'sinuous';

constcounter=observable(0);
constview=()=>h('div','Counter ',counter);

document.body.append(view());
setInterval(()=>counter(counter()+1),1000);

Reactivity

The Sinuousobservablemodule provides a mechanism to store and update the application state in a reactive way. If you're familiar withS.jsorMobxsome functions will look very familiar, in under1kBSinuous observable is not as extensive but offers a distilled version of the same functionality. It works under this philosophy:

Anything that can be derived from the application state, should be derived. Automatically.

import{observable,computed,subscribe}from'sinuous/observable';

constlength=observable(0);
constsquared=computed(()=>Math.pow(length(),2));

subscribe(()=>console.log(squared()));
length(4);// => logs 16

Use a custom reactive library

Sinuous can work with different observable libraries; S.js, MobX, hyperactiv. See thewiki for more info.

Hydration

Sinuoushydrateis a small add-on that provides fast hydration of static HTML. It's used for adding event listeners, adding dynamic attributes or content to existing DOM elements.

In terms of performance nothing beats statically generated HTML, both in serving and rendering on the client.

You could say using hydrate is a bit like usingjQuery,you'll definitely write less JavaScript and do more. Additional benefits with Sinuous is that the syntax will be moredeclarativeandreactivityis built-in.

import{observable}from'sinuous';
import{hydrate,dhtml}from'sinuous/hydrate';

constisActive=observable('');

hydrate(
dhtml`<a class= "navbar-burger burger${isActive}"
onclick=${()=>isActive(isActive()?'':' is-active')}/>`
);

hydrate(dhtml`<a class= "navbar-menu${isActive}"/>`);

Internal API

Sinuous exposes an internal API which can be overridden for fun and profit. For examplesinuous-contextuses it to implement a React like context API.

As of0.27.4the internal API should be used to make Sinuous work with a 3rd party reactive library likeMobx.This can be done by overridingsubscribe,root,sampleandcleanup.

Example

import{api}from'sinuous';

constoldH=api.h;
api.h=(...args)=>{
console.log(args);
returnoldH(...args);
};

Methods

These are defined insinuous/srcandsinuous/h.

  • h(type: string, props: object,...children)
  • hs(type: string, props: object,...children)
  • insert<T>(el: Node, value: T, endMark?: Node, current?: T | Frag, startNode?: Node): T | Frag;
  • property(el: Node, value: unknown, name: string, isAttr?: boolean, isCss?: boolean): void;
  • add(parent: Node, value: Value | Value[], endMark?: Node): Node | Frag;
  • rm(parent: Node, startNode: Node, endMark: Node): void;
  • subscribe<T>(observer: () => T): () => void;
  • root<T>(fn: () => T): T;
  • sample<T>(fn: () => T): T;
  • cleanup<T extends () => unknown>(fn: T): T;

Note thatsomeobservable methods are imported into the internal API fromsinuous-observablebecause they're used in Sinuous' core. To access all observable methods, import fromsinuous/observabledirectly.

Concept

Sinuous started as a little experiment to get similar behavior asSurplusbut with template literals instead of JSX. HTMcompiles to anhtag. Adapted code fromRyan Solid's dom expressions + a Reactive library provides the reactivity.

Sinuous returns ahyperscriptfunction which is armed to handle the callback functions from the reactive library and updates the DOM accordingly.

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]