Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

sinuous-dup

luwes34MIT0.27.12TypeScript support: included

🧬 Small, fast, reactive render engine

functional, reactive, declarative

readme

Sinuous

Version Badge size codecov Financial Contributors on Open Collective

npm: npm i sinuous
cdn: https://unpkg.com/sinuous
module: https://unpkg.com/sinuous?module


  • Small. hello world at ~1.4kB gzip.
  • Fast. top ranked of 80+ UI libs.
  • Truly reactive. automatically derived from the app state.
  • DevEx. no compile step needed, choose your view 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
Badge size sinuous/data Enrich plain HTML with data in JS

All-in-one

Size Name Description
Badge size sinuous/all All modules in one bundle for easy use with a <script> tag

cdn: https://unpkg.com/sinuous/dist/all
module: https://unpkg.com/sinuous/module/all

Community

Examples


See complete docs, or in a nutshell...

View syntax

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

Hyperscript directly call h(type: string, props: object, ...children).

Tagged templates transform the HTML to h calls at runtime w/ the html` tag or, at build time with [sinuous/babel-plugin-htm`](./packages/sinuous/babel-plugin-htm).

JSX transforms at build time like React does via @babel/plugin-transform-react-jsx. See @heyheyhello/sinuous-tsx-example for JSX and TSX examples.

Handlebars/Mustache is possible with Hyperstache. See issue #49.


Counter Example (1.4kB gzip) (Codesandbox)

import { observable, html } from 'sinuous';

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

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

JSX

import { h, observable } from 'sinuous';

const counter = observable(0);
const view = () => <div>Counter {counter}</div>;

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

Hyperscript

import { h, observable } from 'sinuous';

const counter = observable(0);
const view = () => h('div', 'Counter ', counter);

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

Reactivity

The Sinuous observable module provides a mechanism to store and update the application state in a reactive way. If you're familiar with S.js or Mobx some functions will look very familiar, in under 1kB Sinuous 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';

const length = observable(0);
const squared = 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 the wiki for more info.

Hydration

Sinuous hydrate is 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 using jQuery, you'll definitely write less JavaScript and do more. Additional benefits with Sinuous is that the syntax will be more declarative and reactivity is built-in.

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

const isActive = 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 example sinuous-context uses it to implement a React like context API.

As of 0.27.4 the internal API should be used to make Sinuous work with a 3rd party reactive library like Mobx. This can be done by overriding subscribe, root, sample and cleanup.

Example

import { api } from 'sinuous';

const oldH = api.h;
api.h = (...args) => {
  console.log(args);
  return oldH(...args);
};

Methods

These are defined in sinuous/src and sinuous/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 that some observable methods are imported into the internal API from sinuous-observable because they're used in Sinuous' core. To access all observable methods, import from sinuous/observable directly.

Motivation

The motivation for Sinuous was to create a very lightweight UI library to use in our video player at Vimeo. The view layer in the player is rendered by innerHTML and native DOM operations which is probably the best in terms of performance and bundle size. However the need for a more declarative way of doing things is starting to creep up. Even if it's just for ergonomics.

The basic requirements are a small library size, small application size growth, fast TTI, not crucial but good render performance (creating & updating of DOM nodes).

More importantly, the developer experience. Working close to the metal with as few specialized syntaxes as possible is a key goal for Sinuous. The html` tag returns a nativeNode` instance and the components are nothing more than simple function calls in the view.

Another essential aspect is modularity, Sinuous is structured in a way that you only pay for what you use.

Concept

Sinuous started as a little experiment to get similar behavior as Surplus but with template literals instead of JSX. HTM compiles to an h tag. Adapted code from Ryan Solid's dom expressions + a Reactive library provides the reactivity.

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

Browser Support

Sinuous supports modern browsers and IE11+ (requires Array.from polyfill if using sinuous/map, sinuous/render, sinuous/template, or sinuous/data).

Sauce Test Status

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]

Big Thanks

Cross-browser Testing Platform and Open Source ❤ Provided by Sauce Labs

changelog

Change Log

This project adheres to Semantic Versioning.

0.28.0 - 2022-01-30

Added

  • Added back in the old map diff algorithm #192

0.27.12 - 2021-02-20

Added

0.27.11 - 2020-10-20

Added

Removed

  • Remove seemingly unneeded code - 0.27.11-beta.0 #143

0.27.10 - 2020-10-13

Added

0.27.9 - 2020-09-25

Fixed

  • Fixed an issue with hydrating components with children.

0.27.8 - 2020-09-02

Fixed

  • Fix template tags for event handlers fix #130

0.27.7 - 2020-07-11

Removed

  • Removed sinuous/map/mini module.

Fixed

  • Permit observables as values in style objects in Typescript + JSX #123 thanks @lynlevenick 💛

0.27.6 - 2020-06-27

Fixed

  • Fix typings in api.s and npm package. Revert JSX's d.ts rename #119

0.27.5 - 2020-06-20

Removed

  • Removed sinuous/memo module. This is available as a standalone module at luwes/memo.

0.27.4 - 2020-06-20

Added

  • Typescript definition changes and explaining Sinuous internals #114 thanks @heyheyhello 💛

0.27.3 - 2020-05-23

Added

  • Update babel-plugin-htm to 3.0.0 and simplify upstream diff #110 thanks @heyheyhello
  • Improve typings in observable, computed, and map #111 thanks @heyheyhello 💛

0.27.2 - 2020-05-12

Fixed

  • Fixed hydrate empty child bug 0d66864

0.27.1 - 2020-05-04

Fixed

  • Fixed hydrate conditional root element for real
  • Fixed JSX observable types #105.

0.27.0 - 2020-04-25

Added

  • Add lighter but equaly fast map. #102 Minimal Todo app is just 2.2kB instead of 2.62kB 🏌️‍♂️
  • Fix hydrate conditional root element w/ children 81cb57c

0.26.2 - 2020-03-29

Fixed

  • Fixed nested fragment issue #88.

0.26.1 - 2020-03-25

Fixed

  • Fixed sample argument issue when fn would be an observable.

0.26.0 - 2020-03-25

Removed

  • Removed explicit cleanup of dom element listeners 0791cf9

Added

  • Added observable on function #82.

0.25.2 - 2020-03-16

Changed

  • Added call context to partial attributes
  • Fixed hydrate element w/ children bug
  • Fixed hydrate regression

0.25.1 - 2020-03-11

Changed

  • Fixed a treeshake issue #76.
  • Fixed data propName scope issue.

0.25.0 - 2020-03-09

Changed

  • Removed support for IE9 and IE10.
  • Use of Array.from() in the library (requires a polyfill in IE11) If you need IE11 support it's almost implied you'll need an Array.from() polyfill. It's used enough in Sinuous to replace the hacky [].slice.call(arrayLike).
  • Golf down bytes in hydrate going from 1.07kB to 989B ⛳️

0.24.3 - 2020-03-06

Added

  • Added support for hydrating conditional root elements.

0.24.2 - 2020-02-19

Fixed

  • Fixed sourcemaps by adding rollup-plugin-sourcemaps.

0.24.1 - 2020-02-18

Added

  • Added insert into empty node support for hydrate.

0.24.0 - 2020-02-06

Added

  • Reordered arguments of api.property() #69
  • Added lazy executed component children #68

0.23.3 - 2020-02-02

Fixed

  • Fixed nested transactions #66

0.23.2 - 2020-02-02

Fixed

  • Fixed minify bug `._propName
  • Fixed observable types #65

0.23.1 - 2020-01-31

Fixed

  • Fixed false positive for clone flag in template.

0.23.0 - 2020-01-31

Added

  • Added auto-import pragma option to sinuous/babel-plugin-htm. For example:
plugins: [
  [
    'sinuous/babel-plugin-htm',
    {
      import: 'sinuous',
    },
  ],
  [
    'sinuous/babel-plugin-htm',
    {
      pragma: 'hs',
      tag: 'svg',
      import: 'sinuous',
    },
    'svg',
  ],
];

0.22.0 - 2020-01-30 - BREAKING CHANGES

Changed

  • Renamed html and svg to dhtml and dsvg in sinuous/hydrate.

Added

  • Added sinuous/render module for top down render functions.

0.21.4 - 2020-01-24

Fixed

  • Fixed an issue where the clearing of a DOM node that was previously pulled out or moved in the DOM cleared siblings before the content.
  • Fixed memoizing by adding support for Arrays and DOM nodes to be serialized.

0.21.3 - 2020-01-17

0.21.2 - 2020-01-17

0.21.1 - 2020-01-17

Fixed

  • Fixed memo issue.

0.21.0 - 2020-01-17

Added

  • Added sinuous/memo module.

0.20.1 - 2020-01-15

Fixed

  • Fixed some issues with the new data module.

0.20.0 - 2020-01-10

Added

  • Added sinuous/data module #53
  • Improved sinuous/template, accepts objects and functions as data.

0.19.3 - 2020-01-04

Fixed

  • Fixed hydrate root props handling.

Added

  • Hydrate components support.

0.19.2 - 2020-01-02

Added

  • Added element context to accessor calls in property and insert api.

0.19.1 - 2020-01-01

Fixed

  • Fixed CSS selector escaping in hydrate.

Added

  • Added types for context() and internal api methods.
  • Added tag name selecting in hydrate, for example:
hydrate(html` <body onclick="${blur}"></body> `);

0.19.0 - 2019-12-25

Added

  • Added type declarations #52

0.18.5 - 2019-12-15

Added

  • Added special bundle to display bundle size of Sinuous w/ observable included.

0.18.4 - 2019-12-13

Changed

  • Use Sinuous api instead of template hooks and golf some bytes 🏌️‍♂️

0.18.3 - 2019-12-13

Removed

  • Golf down some bytes in map (-68 B) ⛳️

0.18.2 - 2019-12-07

Fixed

  • Fixed memory leak in observable #51

0.18.1 - 2019-12-03

Added

  • Add S alias for the all package which is meant to include in your webpage with a script tag.

0.18.0 - 2019-12-03

Added

  • Added all package for easy use with a script tag.

0.17.2 - 2019-11-28

0.17.1 - 2019-11-28

Fixed

  • Fixed hydrate content function that returns a stringable.

0.17.0 - 2019-11-24

Added

  • Added implicit root selector to hydrate.

0.16.3 - 2019-11-03

Added

  • Added sourcemaps to all bundles

0.16.2 - 2019-11-02

0.16.1 - 2019-11-02

Fixed

0.16.0 - 2019-10-06

Added

  • Added partial attribute support #40

0.15.3 - 2019-10-03

Fixed

  • Added better support for adjacent text nodes in hydrate.

0.15.2 - 2019-09-28

Fixed

  • Fixed babel-plugin-htm TypeError: Cannot read property '0' of null.

Changed

  • Made property api accept plain objects.

0.15.1 - 2019-09-25

Changed

  • Changed map mini import to be import { map } from 'sinuous/map/mini' instead of import { mini } from 'sinuous/map'.
  • Re-named variables in map and golfed down the bundle from 1.45kB to 1.19kB, also by removing the fast path for swap backward and swap forward.

0.15.0 - 2019-09-13

Added

  • Added basic hydrate feature #36
  • Disable observable cleaning by default in map for repeated template's.

0.14.2 - 2019-09-04

Fixed

  • Prevented running duplicate nested computations.
  • Marked removed children computations as fresh so they skip running.
  • Fixed a transaction bug which prevent nullish values from being set.
  • Improved performance by using Set for the subscribers #20

Added

  • Added import { computed } from 'sinuous/observable' which S is now an alias for.

0.14.1 - 2019-08-29

Fixed

  • Fixed template scope key actions bug. Impacted performance and incorrect rendering. Visible in JS Framework Benchmark.

Changed

0.14.0 - 2019-08-27 - BREAKING CHANGES

Added

  • Added import { mini } from 'sinuous/map'. It's only 570 bytes heiiii! #32

Gob

Changed

  • Breaking changes import map from 'sinuous/map' to import { map } from 'sinuous/map'

0.13.1 - 2019-08-11

Fixed

  • Fixed same key observe tags in template fix #5

Added

  • Added transaction method #21

0.13.0 - 2019-08-03

Added

  • Added web ES modules support #27

Fixed

0.12.9 - 2019-07-31

Fixed

0.12.8 - 2019-07-31

Added

  • Added HTM's support for HTML comments
  • Added out of the box support down to IE9

Fixed

0.12.7 - 2019-07-28

Fixed

0.12.6 - 2019-07-28

Fixed

  • Fixed naming issue for umd and iife bundles
  • Add observable duplicate edges check
  • Replace Map w/ array for map disposers

0.12.5 - 2019-07-23

Fixed

0.12.4 - 2019-07-23

Fixed

0.12.3 - 2019-07-22

Fixed

Changed

  • Upgraded sinuous/htm and sinuous/babel-plugin-htm to latest version.

0.12.2 - 2019-07-21

Added

  • Added support for plain array inserts again w/ no size increase.

0.12.1 - 2019-07-19

Changed

  • Make map return a DocumentFragment.
  • Optimized and re-used code for full replace in map.

0.12.0 - 2019-07-17

Added

  • Added SVG support.

Removed

  • Removed plain array inserts. Fragments can be used for this aka
html`
  <h1>Title</h1>
  <p>Some text</p>
`;

0.11.4 - 2019-07-07

Changed

  • Prioritized string and numeric values for insert.

0.11.3 - 2019-07-07

Removed

  • Removed array to node support in insert. Save some bytes.

0.11.2 - 2019-07-07

Changed

  • Fixed mixed exports It's not recommended to have a default AND named exports

Removed

  • Removed multi expressions

0.11.1 - 2019-07-04

Added

  • Added minified browser version .min.js.

Removed

  • Removed shorthand tag parser from hyperscript.
  • Removed specialized attribute ref. No need w/ Sinuous since it returns plain elements.
  • Removed unneeded api on h tag.

Changed

  • Optimized rollup/terser config for smaller bundle.

0.11.0 - 2019-07-03

Added

  • Added sinuous/htm and sinuous/babel-plugin-htm supporting fragments.
  • Added easy import import { html } from 'sinuous';

0.10.1 - 2019-07-02

Changed

  • Removed specialized attribute bindings.
  • Removed specialized Capture syntax.
  • Used Sinuous's normalizeArray in map, save bytes.

0.10.0 - 2019-06-30 - BREAKING CHANGES

Changed

  • Removed classList property handler, this can be done with a function.
  • Removed specialized events property, HTM supports spread props.
  • Removed initial execution of non observable event handlers so there is now no need for the extra closure. Fixes #3
  • Removed Date, RegExp coercion This can be done in the view.

0.9.2 - 2019-06-29

Changed

  • Removed named: true in rollup config to fix default import.

0.9.1 - 2019-06-29

Changed

  • Optimized template insert action w/ Text.data
  • Changed .mjs to .esm.js to fix submodule issue

0.9.0 - 2019-06-22

Removed

  • Removed support for the legacy observable library It has some limitations:
    • Not possible to store a function because passing a function as argument of the observable setter acts as the subscribe function. Because of this every observable has its own subscribe function.
    • Needs extra cleanup logic

Added

  • First release