Sinuous Cheat Sheet
Sinuous is a JavaScript library designed to make building user interfaces easier and more enjoyable. It offers several key advantages:
- Lightweight: Sinuous has a small footprint, making it ideal for web applications where performance is critical.
- Reactive: Sinuous automatically updates the UI whenever the underlying data changes, eliminating the need for manual DOM manipulation.
- Declarative: With Sinuous, you write UI code that describes what you want, and Sinuous takes care of making it happen. This leads to cleaner and more maintainable code.
- Choice of view syntax: Sinuous supports both tagged templates and JSX, allowing you to choose the syntax that you are most comfortable with.
- Add-ons: Sinuous has a number of add-ons that provide additional functionality, such as routing and hydration. These add-ons can make it easier to build complex web applications.
If you’re looking for a lightweight, reactive, and easy-to-use UI library, then Sinuous is definitely worth checking out!
State Management:
- Observables: Used to store and manage application state. Imported from
sinuous/observable. - API: Powerful yet lightweight.
- Reactivity: Changes in observables automatically update the UI.
Concept:
- Observable stores data (
name = o('World')). - Subscribe to listen for changes (
subscribe(() => ...)). - Callback updates the UI based on the observable’s value.
- Set new values to the observable to trigger updates (
name('Dude')).
Example:
import { o, html } from 'sinuous';
const name = o('World');document.body.append( html` <h1>Hello, ${name}!</h1> `);Real-World:
- Sinuous handles subscriptions internally, simplifying development.
- No need for manual VDOM diffing.
Benefits:
- Simple and efficient state management.
- Automatic UI updates on state changes.
- Lightweight and performant.
Rendering
Section titled “Rendering”Rendering:
- Uses
hyperscript-like function calls:h(tag, props, children) - Simplified tagged templates with
htm - Compile-time transformation preferred for production (
sinuous/babel-plugin-htm)
Example:
import { html } from 'sinuous';
const element = html` <h1>Hello world!</h1>`;
// Translated to:const element = h('h1', 'Hello world!');Observables:
- Key to reactivity, represent state and content.
- Single template expression for simple state:
const property = o('box');const content = o('This is some text.');
const p = html` <p class=${property}> ${content} </p>`;- Function calls needed to retrieve observable values in complex expressions:
const length = o(0);
const div = html` <div>${length} squared: ${() => Math.pow(length(), 2)}</div>`;Properties:
- Sets
propertieson DOM elements, not HTML attributes. - Camel-cased attributes (e.g.,
colSpan). - Special cases for some attributes (e.g.,
htmlFor).
Events:
- Functions starting with
onbecome event listeners:
html` <a href="#" onclick=${() => alert('you are 1,000,000th visitor!')}> Click here to win a prize </a>`;Styles:
- Accepts object or string:
html` <h1 style={{ 'font-family': 'Comic Sans', color: 'springgreen' }}> Happy Birthday! </h1>`;Further Exploration:
sinuous/htmdocumentation for advanced uses.
Remember, compile-time transformation is recommended for production!
Components
Section titled “Components”Basics:
- Split UI into reusable, isolated code pieces.
- Define as plain JS functions.
- Use
<${Component} />in views. - Return a regular HTML element (no render function needed).
- Pass props via attributes.
Example:
import { o, html } from 'sinuous';
const Timer = (props) => { const seconds = o(0); return html` <div>${props.unit}: ${seconds}</div> `;};
document.body.append( html` <${Timer} unit="Seconds" /> `);Composing Components:
- Create components for any level of detail.
- Reuse and nest components endlessly.
Example:
function Welcome(props) { return html` <h1>Hello ${props.name}</h1> `;}
function App() { return html` <div> <${Welcome} name="Sara" /> <${Welcome} name="Cahal" /> <${Welcome} name="Edite" /> </div> `;}
document.body.append( html` <${App} /> `);Advanced Example:
function Comment(props) { return html` <div class="comment"> <${UserInfo} user=${props.author} /> <div class="comment-text"> ${props.text} </div> <div class="comment-date"> ${formatDate(props.date)} </div> </div> `;}
function UserInfo(props) { return html` <div class="userInfo"> <${Avatar} user=${props.user} /> <div class="userInfo-name"> ${props.user.name} </div> </div> `;}
function Avatar(props) { return html` <img class="avatar" src=${props.user.avatarUrl} alt=${props.user.name} /> `;}Remember:
- Components promote code reusability and organization.
- Think logically about UI breakdown into components.
- Explore Sinuous documentation for advanced features and techniques.
Rendering Lists:
- Two approaches: with and without the
mapmodule.
Without map:
- Re-renders entire list on changes.
- Useful for small lists or static lists with dynamic content.
Example:
import { o, html } from 'sinuous';
const list = [/* ... */];const view = () => html` <ul> ${list.map(item => html`<li>${item}</li>`)} </ul>`;With map:
- Efficiently handles list changes (adds, removes, updates).
- Minimizes DOM manipulations through diffs.
- Recommended for dynamic lists.
Example:
import { o, html } from 'sinuous';import { map } from 'sinuous/map';
const list = o(/* ... */);const view = () => html` <ul> ${map(list, item => html`<li>${item.text}</li>`)} </ul>`;Key Points:
mapis optimized for smooth, performant list rendering.- Use
mapfor dynamic lists to avoid unnecessary DOM updates. - Monitor DOM inspector to observe optimizations by the diff engine.
Further Exploration:
sinuous/mapdocumentation for advanced usage.- Codesandbox examples for hands-on practice.
Remember, map is your ally for efficient and snappy list handling in Sinuous!
Hydration
Section titled “Hydration”Hydrate: Adding Interactivity to Static HTML
- Quickly add event listeners and dynamic content to existing HTML.
- Perfect for static site generators (e.g., Eleventy).
- More declarative and reactive than jQuery.
Example:
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}" />`);Key Points:
- Creates virtual tree for dynamic parts.
- Infers root element from
idorclassattributes. - Minimizes duplication of static content.
API:
hydrate(tree, [root]): hydrates root node with dynamic HTML.dhtml`` ord()`: creates virtual HTML tree structure.dsvg`` ords()`: creates virtual SVG tree structure._: placeholder for skipped static content.
Usage:
- Define dynamic attributes only.
- Use
hydrateto add event listeners and reactive content.
Benefits:
- Improved performance compared to full client-side rendering.
- Declarative and reactive approach.
- Less JavaScript code needed.
Remember, hydrate is a powerful tool for adding interactivity to static HTML in Sinuous!