Components
A Rivet component connects a piece of server-rendered HTML with behaviour. It is
defined with defineComponent, registered on the app, and instantiated on
mount().
defineComponent
Section titled “defineComponent”defineComponent is just a type-safe identity wrapper: it takes a factory
function and returns it unchanged. The benefit is type inference for the context
and the returned API.
import { defineComponent } from "@fullhaus/rivet";
export const counter = defineComponent(({ part, signal, on, effect }) => { const output = part("output"); const count = signal(0);
on(part("inc"), "click", () => count.update((v) => v + 1)); effect(() => { output.textContent = String(count()); });
return { reset: () => count.set(0) };});The factory receives one argument: the component context. Whatever it returns becomes the instance’s public API.
Register & mount
Section titled “Register & mount”import { createRivet } from "@fullhaus/rivet";import { counter } from "./counter";
const app = createRivet() .component("counter", counter) // name must match data-rivet="counter" .mount();The name given to component(name, …) must match the data-rivet attribute in
the HTML. On mount():
- all
[data-rivet]elements are found, - the matching factory is looked up by name,
- a context is created and the factory is called,
- the returned API is registered under the
data-rivet-id(if present).
Already-mounted elements are skipped — mount() is idempotent and can be called
again after loading more HTML.
Parts: elements inside the root
Section titled “Parts: elements inside the root”Inside the component root, child elements are addressed via data-rivet-part:
<div data-rivet="counter" data-rivet-id="c1"> <output data-rivet-part="output">0</output> <button data-rivet-part="inc">+1</button></div>const output = part("output"); // exactly one — throws if not foundconst buttons = parts("inc"); // array, possibly emptypart(name)returns exactly one element and throws an error with component context if none exists.parts(name)returns all matching elements as an array.
Multiple instances
Section titled “Multiple instances”The same component can appear in the document any number of times. Each root becomes an independent instance with its own state and its own cleanup.
<div data-rivet="counter" data-rivet-id="left">…</div><div data-rivet="counter" data-rivet-id="right">…</div>app.require("left").reset();app.require("right").reset();Public API
Section titled “Public API”The factory’s return value is the public API. Components without an ID still work (behaviour runs), their API is just not retrievable from the outside.
const menu = app.require<{ hide(): void }>("main-menu");menu.hide();Pass a type parameter to get/require to use the API in a type-safe way.