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
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
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
properties
on DOM elements, not HTML attributes. - Camel-cased attributes (e.g.,
colSpan
). - Special cases for some attributes (e.g.,
htmlFor
).
Events:
- Functions starting with
on
become 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/htm
documentation for advanced uses.
Remember, compile-time transformation is recommended for production!
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.
Lists
Rendering Lists:
- Two approaches: with and without the
map
module.
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:
map
is optimized for smooth, performant list rendering.- Use
map
for dynamic lists to avoid unnecessary DOM updates. - Monitor DOM inspector to observe optimizations by the diff engine.
Further Exploration:
sinuous/map
documentation for advanced usage.- Codesandbox examples for hands-on practice.
Remember, map
is your ally for efficient and snappy list handling in Sinuous!
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
id
orclass
attributes. - Minimizes duplication of static content.
API:
hydrate(tree, [root])
: hydrates root node with dynamic HTML.dhtml`` or
d()`: creates virtual HTML tree structure.dsvg`` or
ds()`: creates virtual SVG tree structure._
: placeholder for skipped static content.
Usage:
- Define dynamic attributes only.
- Use
hydrate
to 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!