Build · core model
Components
A Presolve component is an exported class that extends Component and returns TSX from one instance render() method. Extending the base class is compiler evidence; there is no runtime component registry.
Define a component
import { Component } from "presolve";export class Welcome extends Component {name = "Ada";render() {return <section><h1>Hello, {this.name}</h1></section>;}}Ordinary initialized fields are fixed component data. Use state() only when the value must change through an action. The compiler resolves every rendered binding to the owning component instance.
Inputs and defaults
Undecorated instance fields declare inputs. A definite-assignment field is required from the caller; an initializer supplies a default.
export class UserCard extends Component {user!: { name: string };compact = false;render() {return <article>{this.user.name}</article>;}}The component that declares a mutable state field owns its writes. A child can request a change through an explicit action input; it does not reach into its parent’s state.
Invoke components
Use a local or imported PascalCase class directly in TSX. The compiler resolves the class, invocation, input bindings, instance identity, and nested lifecycle.
export class ProfilePage extends Component {profile = { name: "Ada" };render() {return <main><UserCard user={this.profile} /></main>;}}Conditional and keyed structure
A conditional creates one compiler-owned structural host. A keyed map() retains identity by key while items reorder, appear, or leave.
return <section>{this.visible ? <ProfileCard /> : <p>Hidden</p>}<ul>{this.items.map((item) =><li key={item.id}>{item.label}</li>)}</ul></section>;Use condition && <Panel /> or condition ? (<Panel />) : null when the false branch should emit no DOM. Presolve publishes an empty false fragment and materializes the true branch through the same compiler-owned host.
Identity and teardown
Instance identity is not inferred from DOM position at runtime. Static, conditional, keyed, nested, and slot-projected occurrences receive compiler-issued ownership. This makes retention and child-first teardown deterministic across cold execution and resume.
Beta limits
- Component classes must resolve statically; dynamic component expressions are outside the beta.
- Constructors, mixins, and runtime registries do not establish component semantics.
- Keyed lists require an admitted
map()shape and stable primitive keys. - Unsupported control flow fails during
pnpm check; Presolve does not switch to generic reconciliation.