Simple and elegant component-based UI library
—
Core functionality for mounting components to DOM elements and managing their lifecycle. Components go through mount, update, and unmount phases with corresponding lifecycle hooks.
Mounts registered components to DOM elements, creating component instances.
/**
* Mounting function for globally registered components
* @param selector - CSS selector string or HTMLElement to mount to
* @param initialProps - Initial properties to pass to the component
* @param componentName - Optional specific component name to mount
* @returns Array of mounted component instances
*/
function mount<Props extends DefaultProps, State extends DefaultState>(
selector: string | HTMLElement,
initialProps?: Props,
componentName?: string
): RiotComponent<Props, State>[];Usage Examples:
import { mount, register } from "riot";
// Mount all registered components matching their tag names
const components = mount("my-component");
// Mount with initial props
const components = mount("my-timer", { startTime: 10 });
// Mount to specific element
const element = document.getElementById("app");
const components = mount(element, { title: "Hello World" });
// Mount specific component to elements
const components = mount(".widget", { theme: "dark" }, "my-widget");Unmounts component instances from DOM elements, cleaning up resources and optionally preserving the root element.
/**
* Unmounting helper function for components mounted to DOM nodes
* @param selector - CSS selector string or HTMLElement to unmount from
* @param keepRootElement - If true, preserves the root DOM element
* @returns Array of DOM elements that were unmounted
*/
function unmount(
selector: string | HTMLElement,
keepRootElement?: boolean
): HTMLElement[];Usage Examples:
import { unmount } from "riot";
// Unmount all components in matching elements
const elements = unmount("my-component");
// Unmount but keep the DOM element
const elements = unmount(".widgets", true);
// Unmount from specific element
const element = document.getElementById("app");
const elements = unmount(element);Components follow a predictable lifecycle with hooks for each phase:
interface RiotComponent<Props = DefaultProps, State = DefaultState> {
/** Called before component is mounted to DOM */
onBeforeMount?(props: Props, state: State): void;
/** Called after component is mounted to DOM */
onMounted?(props: Props, state: State): void;
/** Called before component state/props update */
onBeforeUpdate?(props: Props, state: State): void;
/** Called after component state/props update */
onUpdated?(props: Props, state: State): void;
/** Called before component is unmounted from DOM */
onBeforeUnmount?(props: Props, state: State): void;
/** Called after component is unmounted from DOM */
onUnmounted?(props: Props, state: State): void;
/** Optional method to control when component should update */
shouldUpdate?(newProps: Props, oldProps: Props): boolean;
}Once mounted, component instances provide methods for state management:
interface RiotComponent<Props = DefaultProps, State = DefaultState> {
/** Mount component to DOM element */
mount(
element: HTMLElement,
initialState?: State,
parentScope?: object
): RiotComponent<Props, State>;
/** Update component state and re-render */
update(
newState?: Partial<State>,
parentScope?: object
): RiotComponent<Props, State>;
/** Unmount component from DOM */
unmount(keepRootElement?: boolean): RiotComponent<Props, State>;
/** Query single element within component root */
$(selector: string): Element | null;
/** Query multiple elements within component root */
$$(selector: string): Element[];
}Component instances have read-only properties and mutable state:
interface RiotComponent<Props = DefaultProps, State = DefaultState> {
/** Read-only props passed to component */
readonly props: Props;
/** Root DOM element of the component */
readonly root: HTMLElement;
/** Component name (if registered) */
readonly name?: string;
/** Slot data for child content */
readonly slots: TagSlotData[];
/** Mutable component state */
state: State;
/** Optional child component mappings */
components?: RiotComponentsMap;
}Usage Example:
const componentDef = {
onBeforeMount(props, state) {
this.state = { count: props.initial || 0 };
},
onMounted(props, state) {
console.log("Component mounted with state:", state);
},
increment() {
this.update({ count: this.state.count + 1 });
},
onBeforeUpdate(props, state) {
console.log("Updating from", this.state.count, "to", state.count);
},
onUnmounted() {
console.log("Component cleaned up");
}
};type DefaultProps = Record<PropertyKey, any>;
type DefaultState = Record<PropertyKey, any>;
type TagSlotData = {
id: string;
html: string;
bindings: BindingData[];
};
type RiotComponentsMap = {
[key: string]: RiotComponentWrapper<RiotComponent>;
};Install with Tessl CLI
npx tessl i tessl/npm-riot