Adding a UI
Extending the Service
Section titled “Extending the Service”Rimitive provides the view package with view-specific modules. el() is the foundation for creating elements. It’s renderer-agnostic, but we’ll use the DOM adapter here.
Add the view modules to your service:
import { compose } from '@rimitive/core';import { SignalModule, ComputedModule, EffectModule } from '@rimitive/signals/extend';import { createDOMAdapter } from '@rimitive/view/adapters/dom';import { createElModule } from '@rimitive/view/el';import { MountModule } from '@rimitive/view/deps/mount';
export const svc = compose( SignalModule, ComputedModule, EffectModule, createElModule(createDOMAdapter()), MountModule);export type Service = typeof svc;Creating Elements
Section titled “Creating Elements”el() is curried: el(tag)(children):
// A div tagel('div')();
// An h1 with text ("Hello, World" are children)el('h1')('Hello, World');
// A div with `el` childrenel('div')( el('h1')('Title'), el('p')('Some text'));el returns a UI spec with a stable reference to the underlying element.
Use .props() to set reactive attributes and events:
const isDisabled = signal(false);
const button = el('button').props({ className: 'primary', disabled: isDisabled, onclick: () => console.log('clicked'),})('Click me');
isDisabled(true); // button becomes disabled, class changesProps are type-safe, driven by the adapter provided.
Reactive Content
Section titled “Reactive Content”Pass a computed for reactive text:
const count = signal(0);
const display = el('div')( computed(() => `Count: ${count()}`));
count(5); // display updates to "Count: 5"UI Components
Section titled “UI Components”UI components in Rimitive are functions that return UI specs:
const Greeting = (name: string) => el('div')( el('h2')(`Hello, ${name}!`), el('p')('Welcome to Rimitive.') );
const DoubleGreeting = () => el('div')( Greeting('Ada'), Greeting('Grace'));Here’s a working example:
Wrapping Components in Service Functions
Section titled “Wrapping Components in Service Functions”Just like behaviors, components can be wrapped in a service function for portability:
import type { Service } from './service';import { counter } from './behaviors/counter';
const Counter = (svc: Service) => { const { el, computed } = svc; const useCounter = svc(counter);
return (initial: number) => { const { count, doubled, increment, decrement, reset } = useCounter(initial);
return el('div')( el('div')(computed(() => `Count: ${count()} (doubled: ${doubled()})`)), el('div')( el('button').props({ onclick: decrement })('-'), el('button').props({ onclick: increment })('+'), el('button').props({ onclick: reset })('Reset') ) ); };};
// Components can compose other components in the service functionconst Counters = (svc: Service) => { const { el } = svc; const CounterComponent = svc(Counter);
return () => el('div')( CounterComponent(0), CounterComponent(100) );};This is useful for testing, SSR, or sharing components across different service contexts.
Mounting
Section titled “Mounting”Use mount() to actually attach to the DOM:
const App = () => el('div')( el('h1')('My App'));
const app = mount(App());document.body.appendChild(app.element!);