Your First App
This is the first step of a four-part tutorial that builds up a Weft app from a static component to a server-rendered, error-handled one. By the end you will have touched every core idea; each step adds exactly one.
We assume you know Effect fundamentals — Weft is Effect for the UI, and we will not re-explain Effect.gen, services, or streams from scratch.
Install
npm install @weftui/core @weftui/dom effect@weftui/core gives you the element builders and combinators; @weftui/dom renders them (its ./client entry mounts in the browser). effect is the peer everything is built on.
Build a component
A component is a plain function you call — there is no JSX and no <Component/> deferral. It returns a Node, which is just an Effect that produces a DOM node:
import { h } from "@weftui/core";
import { mount } from "@weftui/dom/client";
import { Effect } from "effect";
function App() {
return h.div({ class: "app" }, [h.h1("Hello, Weft"), h.p("A minimal app.")]);
}
void Effect.runPromise(mount(App(), document.getElementById("root")!));The h namespace is the entry point: every property (h.div, h.h1, h.button, …) is a builder for that HTML tag. A builder takes optional props and children, and returns a Node.
What just happened
App()returns aNode<never, never>— the two type parameters are the error channel (E) and the requirement channel (R), bothneverhere because this component neither fails nor needs a service. As your app grows, those channels accumulate what it can fail with and what it depends on. That is the whole point of Weft's types — see The Rendering Model.mount(node, target)renders the node intotarget, building real DOM and starting any reactive streams. It returns anEffect<MountHandle>; run it with your Effect runtime (Effect.runPromiseis fine for a script).- The component function runs once. Nothing here re-runs on a timer or a state change — because there is no state yet. That comes next.
Next
- Reactivity → — make the UI change over time with
SubscriptionRefand streams