Composition
Stator composes at two levels: machines compose into a state graph, and components compose into a UI tree. They’re separate mechanisms with separate jobs.
Two wiring axes
Section titled “Two wiring axes”Machines relate to each other in exactly two ways — one synchronous, one reactive.
reads:
Section titled “reads:”reads: is a synchronous pull. A machine declares the machines it depends on, and inside its transitions it can read their current state:
reads: [ProductsMachine],// inside a transition:do: (ctx, ev, { reads }) => { const product = reads.ProductsMachine.byId(ev.productId) if (product) ctx.items.push({ productId: ev.productId, unitPrice: product.price, quantity: 1 })},reads.ProductsMachine is typed from the declared tuple, with the read machine’s selectors preserved. Reads resolve server-side — which is why a machine that reads another is server-pinned.
subscribes / emits
Section titled “subscribes / emits”emits lets a machine announce a fact; subscribes lets another machine react to it. The emitting machine names domain facts and attaches a payload selector:
emits: { ITEM_ADDED: { payload: cartSnapshot } },subscribes: [ { from: CheckoutMachine, event: 'ORDER_PLACED', dispatch: 'CLEAR' },],Where reads is “I need your state right now,” subscribes/emits is “tell me when this happened.” The payload selector runs after the emitting transition commits, so subscribers see post-transition state.
app vs session machines
Section titled “app vs session machines”A machine’s lifecycle shapes how it composes:
- session→app delivery works (an admin/app machine can subscribe to a session machine’s emits; the runtime injects the originating
sourceSessionId). - app→app is wired at app boot.
Component invocation
Section titled “Component invocation”In a template, a capitalized tag invokes a Stator component; a lowercase tag is a plain HTML element:
<CartPage cart={cart} products={products} /> <!-- component --><section class="cart"> … </section> <!-- HTML element -->Components receive machines and data as props via Stator.props<...>(), and the children render eagerly at the call site.
Named children
Section titled “Named children”A component declares slots for content with <children>, and callers target them with child="...":
<!-- in BaseLayout.stator --><header><children name="header" /></header><main><children /></main><!-- a caller --><BaseLayout> <nav child="header"> … </nav> <!-- fills the "header" slot --> <p>Goes to the default slot.</p></BaseLayout>Note this is <children> + child="...", not the Web Components <slot> element — it’s a compile-time composition feature, resolved in a single eager render pass, with no shadow DOM involved. The compiler validates that a child="x" marker matches a declared <children name="x" />.
.stator route pages
Section titled “.stator route pages”A route page is the same composition model with a frontmatter that uses Stator.reads([...]) instead of Stator.props. It pulls the machines it needs and composes layout and page components in its body — see Routing. The page is a component; “route” just means “discovered under routes/ and given request access.”