@weftui/core 0.27.1 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,9 @@ description: Render a pending indicator (e.g. a top progress bar) during a defer
9
9
 
10
10
  **Goal:** show a progress indicator while a [lazy route](https://weftui.dev/docs/how-to/split-routes-lazily) resolves its chunk and data, so a slow network is visible instead of feeling frozen.
11
11
 
12
- When you navigate to a route, the router is **deferred-commit**: it resolves the target branch's chunk (if the component is `Router.lazy`) **and the matched leaf's own component effect** including any data the leaf awaits in its body _before_ swapping the URL, keeping the previous page mounted for the whole window. That resolve window is exposed as a reactive signal, [`Router.navigating`](https://weftui.dev/docs/reference/router#routernavigating), that you read to render pending UI.
12
+ When you navigate to a route, the router is **deferred-commit**. It resolves the target branch's chunk (if the component is `Router.lazy`) **and the matched leaf's own component effect**, including any data the leaf awaits in its body. Only then does it swap the URL, keeping the previous page mounted for the whole window.
13
+
14
+ That resolve window is exposed as a reactive signal, [`Router.navigating`](https://weftui.dev/docs/reference/router#routernavigating), that you read to render pending UI.
13
15
 
14
16
  ```typescript
15
17
  import { Component, h } from "@weftui/core";
@@ -32,7 +34,7 @@ const Shell = Component.gen(function* () {
32
34
  });
33
35
  ```
34
36
 
35
- Thread the signal into a persistent layout (the outermost `Shell` is ideal, since it never re-renders across navigations), and style the pending class however you like a top bar, a cursor change, a dimmed outlet.
37
+ Thread the signal into a persistent layout (the outermost `Shell` is ideal, since it never re-renders across navigations), and style the pending class however you like: a top bar, a cursor change, a dimmed outlet.
36
38
 
37
39
  ## The signal
38
40
 
@@ -44,22 +46,22 @@ type NavState = { readonly _tag: "Idle" } | { readonly _tag: "Navigating"; reado
44
46
 
45
47
  Read it two ways, mirroring `Router.params` / `Router.paramsStream`:
46
48
 
47
- - `Router.navigating` the `Subscribable<NavState>` on the `Router` service.
48
- - `Router.navigatingStream` an `Effect` resolving that `Subscribable`, for use in a `Component.gen` body (as above).
49
+ - `Router.navigating`: the `Subscribable<NavState>` on the `Router` service.
50
+ - `Router.navigatingStream`: an `Effect` resolving that `Subscribable`, for use in a `Component.gen` body (as above).
49
51
 
50
52
  The `to` field on `Navigating` is the target URL, if you want to label _where_ the app is going.
51
53
 
52
54
  ## Behavior to expect
53
55
 
54
- - **Only navigations with real async work flip it.** A branch with no `Router.lazy` node and a leaf whose effect resolves synchronously (no async work, or a memoized revisit) commits in the same tick, and `navigating` stays `Idle` an entirely eager app never sees `Navigating`, and adding the reader costs nothing.
56
+ - **Only navigations with real async work flip it.** A branch with no `Router.lazy` node and a leaf whose effect resolves synchronously (no async work, or a memoized revisit) commits in the same tick, and `navigating` stays `Idle`. An entirely eager app never sees `Navigating`, and adding the reader costs nothing.
55
57
  - **Latest-wins.** Rapid successive navigations commit only the newest; a superseded navigation never resets the signal (the newer one owns it).
56
58
  - **Back/forward.** `popstate` into a route with async work also resolves before committing, so the indicator shows for browser back/forward too.
57
59
  - **Failure resets it.** A rejected chunk load or a failing leaf pre-run (a typed error such as `notFound()`, or a defect) resets `navigating` to `Idle` (it never sticks on), then surfaces through normal error/defect handling.
58
60
  - **Server renders `Idle`.** Server render is buffered, so `navigating` is a client-only concern; the server supplies a constant `Idle` so the same `Shell` type-checks and renders on both sides.
59
- - **No built-in anti-flash delay.** The signal flips as soon as an async window opens, so a borderline-fast navigation can flash the indicator briefly. If you want to only show it past a threshold, delay the reveal in CSS rather than in the signal e.g. `transition-delay: 200ms` on `.is-navigating` so genuinely fast navigations never flicker.
61
+ - **No built-in anti-flash delay.** The signal flips as soon as an async window opens, so a borderline-fast navigation can flash the indicator briefly. If you want to only show it past a threshold, delay the reveal in CSS rather than in the signal (e.g. `transition-delay: 200ms` on `.is-navigating`), so genuinely fast navigations never flicker.
60
62
 
61
63
  ## See also
62
64
 
63
65
  - [`Router.navigating` API reference](https://weftui.dev/docs/reference/router#routernavigating)
64
- - [Split Routes Lazily](https://weftui.dev/docs/how-to/split-routes-lazily) the `Router.lazy` deferred-commit navigation this reports on
65
- - [examples/router-ssr](https://github.com/stefvw93/weft/tree/main/examples/router-ssr) wires this exact progress bar in its `Shell` (`components/shell.ts`), with a `pending-navigation.browser.test.ts`
66
+ - [Split Routes Lazily](https://weftui.dev/docs/how-to/split-routes-lazily): the `Router.lazy` deferred-commit navigation this reports on
67
+ - [examples/router-ssr](https://github.com/stefvw93/weft/tree/main/examples/router-ssr): wires this exact progress bar in its `Shell` (`components/shell.ts`), with a `pending-navigation.browser.test.ts`
@@ -9,7 +9,7 @@ description: Code-split a route's component into its own chunk with Router.lazy,
9
9
 
10
10
  **Goal:** keep a heavy page's render code (and its dependencies) out of the initial bundle, loading it only when its route is actually rendered.
11
11
 
12
- Wrap the route's `component` in [`Router.lazy`](https://weftui.dev/docs/reference/router#routerlazy). The route **descriptor** (its segment and param schemas) stays eager so the matcher, `href`, and the server's dispatch API still see it statically only the component body is split into its own chunk.
12
+ Wrap the route's `component` in [`Router.lazy`](https://weftui.dev/docs/reference/router#routerlazy). The route **descriptor** (its segment and param schemas) stays eager, so the matcher, `href`, and the server's dispatch API still see it statically. Only the component body is split into its own chunk.
13
13
 
14
14
  ```typescript
15
15
  import { Router } from "@weftui/router";
@@ -21,42 +21,44 @@ Router.route("docs/:category/:slug", {
21
21
  });
22
22
  ```
23
23
 
24
- The chunk loads on the server during render and on the client on navigation; only the **matched branch's** chunks are ever fetched. `E`/`R` are preserved — a lazy route has the exact same channels as the same component declared eagerly, so an unmet service requirement is still a compile error at `Router.router(...)`.
24
+ The chunk loads on the server during render and on the client on navigation. Only the **matched branch's** chunks are ever fetched.
25
+
26
+ `E`/`R` are preserved: a lazy route has the exact same channels as the same component declared eagerly. An unmet service requirement is still a compile error at `Router.router(...)`.
25
27
 
26
28
  ## Make the split real
27
29
 
28
- `Router.lazy` only splits if the dynamic `import()` is the **only eager path** to the heavy module. Keep the `Router.route(…)` descriptor in an eagerly-imported file, and move the component implementation (and its heavy deps) into a separate module referenced _only_ through `Router.lazy(() => import("./impl"))`:
30
+ `Router.lazy` only splits if the dynamic `import()` is the **only eager path** to the heavy module. Keep the `Router.route(…)` descriptor in an eagerly-imported file. Move the component implementation (and its heavy deps) into a separate module referenced _only_ through `Router.lazy(() => import("./impl"))`:
29
31
 
30
32
  ```typescript
31
- // routes.ts eager, tiny: just the descriptor
33
+ // routes.ts: eager and tiny, just the descriptor
32
34
  export const docsRoute = Router.route("docs/:category/:slug", {
33
35
  path: { category: Schema.String, slug: Schema.String },
34
36
  component: Router.lazy(() => import("./doc-page-impl").then((m) => m.DocsPage)),
35
37
  });
36
38
 
37
- // doc-page-impl.ts heavy: pulled into its own chunk, never in the initial graph
39
+ // doc-page-impl.ts: heavy, pulled into its own chunk, never in the initial graph
38
40
  export const DocsPage = Component.gen(function* () {
39
41
  /* renderHast, code highlighting, … */
40
42
  });
41
43
  ```
42
44
 
43
- A descriptor file that still `import`s the impl statically gains nothing the bundler keeps it in the initial graph.
45
+ A descriptor file that still `import`s the impl statically gains nothing: the bundler keeps it in the initial graph.
44
46
 
45
47
  ## What you get for free
46
48
 
47
- - **Flash-free hydration.** On a directly-loaded lazy route, the client re-invokes the same slot, awaits the chunk, and adopts the server DOM in place the first production matches, so nothing is mutated.
48
- - **Blank-free navigation.** Client navigation is **deferred-commit**: the router resolves the target branch's chunk **and the matched leaf's own component effect** _before_ committing the URL, so the previous page stays mounted through both the fetch and any data the leaf awaits, and the swap is a single tick. See [Show Navigation Progress](https://weftui.dev/docs/how-to/show-navigation-progress) for the `Router.navigating` signal this exposes.
49
+ - **Flash-free hydration.** On a directly-loaded lazy route, the client re-invokes the same slot, awaits the chunk, and adopts the server DOM in place. The first production matches, so nothing is mutated.
50
+ - **Blank-free navigation.** Client navigation is **deferred-commit**: the router resolves the target branch's chunk **and the matched leaf's own component effect** _before_ committing the URL. The previous page stays mounted through the fetch and any data the leaf awaits, and the swap is a single tick. See [Show Navigation Progress](https://weftui.dev/docs/how-to/show-navigation-progress) for the `Router.navigating` signal this exposes.
49
51
  - **Synchronous revisits.** `Router.lazy` memoizes its load per slot, so a second visit to a loaded route commits immediately.
50
52
 
51
53
  ## Edge cases
52
54
 
53
- - **Lazy layouts.** A `Router.layout({ component: Router.lazy(...) })` splits too each lazy node in the matched branch is awaited; nodes outside it never load.
54
- - **Chunk-load failure is a defect.** If the `import()` rejects (offline, or a stale client requesting a chunk a new deploy removed), it dies as a defect and surfaces through normal defect handling it never hangs or silently 404s. The rejection is memoized, so the route keeps failing until a reload (the deploy-skew case).
55
- - **Not a lazy _subtree_.** Only the component is lazy; you cannot defer a whole `RouteNode` behind an `import()`, because the matcher needs every leaf's segment and param schema before anything loads.
55
+ - **Lazy layouts.** A `Router.layout({ component: Router.lazy(...) })` splits too. Each lazy node in the matched branch is awaited; nodes outside it never load.
56
+ - **Chunk-load failure is a defect.** If the `import()` rejects (offline, or a stale client requesting a chunk a new deploy removed), it dies as a defect and surfaces through normal defect handling. It never hangs or silently 404s. The rejection is memoized, so the route keeps failing until a reload (the deploy-skew case).
57
+ - **Not a lazy _subtree_.** Only the component is lazy. You cannot defer a whole `RouteNode` behind an `import()`; the matcher needs every leaf's segment and param schema before anything loads.
56
58
 
57
59
  ## See also
58
60
 
59
61
  - [`Router.lazy` API reference](https://weftui.dev/docs/reference/router#routerlazy)
60
- - [Show Navigation Progress](https://weftui.dev/docs/how-to/show-navigation-progress) the deferred-commit `Router.navigating` signal
61
- - [Add Routing](https://weftui.dev/docs/how-to/add-routing) authoring the route tree `Router.lazy` plugs into
62
- - [examples/router-ssr](https://github.com/stefvw93/weft/tree/main/examples/router-ssr) includes a `Router.lazy` page (`lazy-page.ts`) with a browser test
62
+ - [Show Navigation Progress](https://weftui.dev/docs/how-to/show-navigation-progress): the deferred-commit `Router.navigating` signal
63
+ - [Add Routing](https://weftui.dev/docs/how-to/add-routing): authoring the route tree `Router.lazy` plugs into
64
+ - [examples/router-ssr](https://github.com/stefvw93/weft/tree/main/examples/router-ssr): includes a `Router.lazy` page (`lazy-page.ts`) with a browser test
@@ -2,14 +2,14 @@
2
2
  title: Style Reactively
3
3
  order: 10
4
4
  section: how-to
5
- description: Drive inline styles from streams a single property, or a whole style object so the DOM updates in place with CSS transitions.
5
+ description: Drive inline styles from streams (a single property, or a whole style object) so the DOM updates in place with CSS transitions.
6
6
  ---
7
7
 
8
8
  # Style Reactively
9
9
 
10
- **Goal:** animate or react to state in an element's inline style without re-rendering a single CSS property, or a whole style object, driven by a stream.
10
+ **Goal:** animate or react to state in an element's inline style without re-rendering. Drive a single CSS property, or a whole style object, from a stream.
11
11
 
12
- The `style` prop accepts the [`Source`](https://weftui.dev/docs/explanation/reactive-primitives) vocabulary at any level: a property value can be a stream, and you can spread a stream of style objects. CSS `transition` composes naturally, because the renderer mutates the existing node in place.
12
+ The `style` prop accepts the [`Source`](https://weftui.dev/docs/explanation/reactive-primitives) vocabulary at any level. A property value can be a stream, and you can spread a stream of style objects. CSS `transition` composes naturally, because the renderer mutates the existing node in place.
13
13
 
14
14
  ```typescript
15
15
  import { h } from "@weftui/core";
@@ -36,9 +36,9 @@ const AnimatedHue = () => {
36
36
 
37
37
  ## Three modes
38
38
 
39
- 1. **A single property as a stream** as above: one key's value is a `Stream`, the others are static strings. Each stream property is subscribed independently.
40
- 2. **A static object** an ordinary `style: { backgroundColor: "#667eea" }` with no streams; nothing updates.
41
- 3. **A whole style object as a stream** spread a stream that emits complete style objects, merged with static props:
39
+ 1. **A single property as a stream.** As above: one key's value is a `Stream`, the others are static strings. Each stream property is subscribed independently.
40
+ 2. **A static object.** An ordinary `style: { backgroundColor: "#667eea" }` with no streams; nothing updates.
41
+ 3. **A whole style object as a stream.** Spread a stream that emits complete style objects, merged with static props:
42
42
 
43
43
  ```typescript
44
44
  const pulse = Stream.make(1, 0.5).pipe(
@@ -49,15 +49,15 @@ const pulse = Stream.make(1, 0.5).pipe(
49
49
  h.div({ style: { ...pulse, transition: "opacity 0.4s ease-in-out" } }, "Pulse");
50
50
  ```
51
51
 
52
- Each emitted object is merged with the static properties on the element.
53
-
54
52
  ## Notes
55
53
 
56
- - **Property names are camelCase** (`backgroundColor`, `boxShadow`) the same keys as the DOM `style` object.
57
- - **CSS transitions just work.** Because a stream emission patches the DOM node directly (no re-render), the browser applies the `transition` as it would for any style mutation.
58
- - **Pace with `Schedule`.** `Stream.iterate`/`Stream.make` paced by `Stream.schedule(Schedule.spaced(…))` and looped with `Stream.forever` is the idiom for time-based style animation; combine with any Effect timing you like.
54
+ - **Property names are camelCase** (`backgroundColor`, `boxShadow`), the same keys as the DOM `style` object.
55
+ - **CSS transitions just work.** A stream emission patches the DOM node directly (no re-render), so the browser applies the `transition` as it would for any style mutation.
56
+ - **Pace with `Schedule`.** The idiom for time-based style animation: `Stream.iterate`/`Stream.make` paced by `Stream.schedule(Schedule.spaced(…))` and looped with `Stream.forever`. Combine with any Effect timing you like.
57
+ - **Classes have a reactive builder too.** `Props.cx` builds a class string from strings, falsy values, nested arrays, and `{ className: condition }` records, where a condition may be a stream. Merging two bags that both carry `class` concatenates them. See [Compose Behavior and Markup](https://weftui.dev/docs/how-to/compose-behavior-and-markup).
59
58
 
60
59
  ## See also
61
60
 
62
- - [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives) reactive style props and the `Source` vocabulary
63
- - [examples/reactive-styles](https://github.com/stefvw93/weft/tree/main/examples/reactive-styles) per-property and whole-object stream styles with CSS transitions
61
+ - [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives): reactive style props and the `Source` vocabulary
62
+ - [Compose Behavior and Markup](https://weftui.dev/docs/how-to/compose-behavior-and-markup): `Props.cx` and merging `class` across two prop bags
63
+ - [examples/reactive-styles](https://github.com/stefvw93/weft/tree/main/examples/reactive-styles): per-property and whole-object stream styles with CSS transitions
@@ -7,9 +7,9 @@ description: Capture a DOM element with the ref prop into a SubscriptionRef<Opti
7
7
 
8
8
  # Use Element Refs
9
9
 
10
- **Goal:** get a handle to a real DOM element to focus it, measure it, or call an imperative browser API on it.
10
+ **Goal:** get a handle to a real DOM element, to focus it, measure it, or call an imperative browser API on it.
11
11
 
12
- Declare a `SubscriptionRef<Option<HTMLElement>>`, attach it with the `ref` prop, and either **react** to the element appearing (a scoped observer on `SubscriptionRef.changes(ref)`) or **read** it later inside a handler.
12
+ Declare a `SubscriptionRef<Option<HTMLElement>>` and attach it with the `ref` prop. Then either **react** to the element appearing (a scoped observer on `SubscriptionRef.changes(ref)`) or **read** it later inside a handler.
13
13
 
14
14
  ```typescript
15
15
  import { h } from "@weftui/core";
@@ -34,9 +34,9 @@ const AutoFocusInput = () =>
34
34
 
35
35
  ## How it works
36
36
 
37
- - **The `ref` prop** takes a `SubscriptionRef<Option<T>>`. The renderer sets it to `Option.some(element)` **once**, when the element is created so the ref is an `Option`: `None` until mount, `Some(el)` after.
38
- - **React to mount** by observing `SubscriptionRef.changes(ref)`: `Stream.filter(Option.isSome)` waits for the element, `Stream.take(1)` takes just the first appearance, and `Stream.runForEach` does the imperative work. This is the equivalent of a mount effect.
39
- - **Use `Effect.forkScoped`, not `Effect.forkChild`.** `forkScoped` ties the observer fiber to the component's **instance scope** (the ambient `Scope` the renderer provides), so it lives as long as the component is mounted. A bare `Effect.forkChild` binds to the transient component-body fiber and is interrupted the instant the generator returns the observer would never fire.
37
+ - **The `ref` prop** takes a `SubscriptionRef<Option<T>>`. The renderer sets it to `Option.some(element)` **once**, when the element is created. The ref is therefore an `Option`: `None` until mount, `Some(el)` after.
38
+ - **React to mount** by observing `SubscriptionRef.changes(ref)`. `Stream.filter(Option.isSome)` waits for the element, `Stream.take(1)` takes just the first appearance, and `Stream.runForEach` does the imperative work. This is the equivalent of a mount effect.
39
+ - **Use `Effect.forkScoped`, not `Effect.forkChild`.** `forkScoped` ties the observer fiber to the component's **instance scope** (the ambient `Scope` the renderer provides). It lives as long as the component is mounted. A bare `Effect.forkChild` binds to the transient component-body fiber and is interrupted the instant the generator returns, so the observer would never fire.
40
40
 
41
41
  ## Read a ref imperatively
42
42
 
@@ -54,10 +54,12 @@ const scroll = () =>
54
54
 
55
55
  - A plain `Ref` suffices if you **only** read the element imperatively; use `SubscriptionRef` when you need to **react** to it becoming available.
56
56
  - Refs are set once at element creation and are not cleared on unmount.
57
+ - **Several refs can share one element.** `ref` also accepts an array, and every entry receives the element: `h.div({ ref: [measure, focus] })`. `Props.merge` produces such an array when both bags carry a `ref`, so a shared behavior's ref and your own can coexist. See [Compose Behavior and Markup](https://weftui.dev/docs/how-to/compose-behavior-and-markup).
57
58
  - Coming from React: `SubscriptionRef.make<Option<T>>(Option.none())` ↔ `useRef<T>(null)`; the `Stream.filter(Option.isSome)` observer ↔ a `useEffect` mount guard.
58
59
 
59
60
  ## See also
60
61
 
61
- - [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives) `SubscriptionRef` and `SubscriptionRef.changes`
62
- - [Author Components](https://weftui.dev/docs/how-to/author-components) instance scope and `Effect.forkScoped`
63
- - [examples/element-ref](https://github.com/stefvw93/weft/tree/main/examples/element-ref) auto-focus, element measurement, and imperative scroll via refs
62
+ - [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives): `SubscriptionRef` and `SubscriptionRef.changes`
63
+ - [Author Components](https://weftui.dev/docs/how-to/author-components): instance scope and `Effect.forkScoped`
64
+ - [Compose Behavior and Markup](https://weftui.dev/docs/how-to/compose-behavior-and-markup): merging a shared behavior's `ref` with your own
65
+ - [examples/element-ref](https://github.com/stefvw93/weft/tree/main/examples/element-ref): auto-focus, element measurement, and imperative scroll via refs
package/docs/index.md CHANGED
@@ -2,40 +2,42 @@
2
2
 
3
3
  **Reactive UI, woven from Effect.**
4
4
 
5
- Weft is an Effect-native reactive DOM library in the browser and on the server. `Node<E, R>` is `Effect.Effect<ElementDescriptor, E, R>`: every element is an Effect, so error and requirement channels accumulate through the tree, all Effect combinators apply to nodes directly, and services flow from mount through the whole app. Streams drive every update — there is no virtual DOM — and the same tree renders to HTML on the server and `hydrate()`s in place on the client, flash-free. No JSX.
5
+ Weft is an Effect-native reactive DOM library, in the browser and on the server. `Node<E, R>` is `Effect.Effect<ElementDescriptor, E, R>`: every element is an Effect. Error and requirement channels accumulate through the tree, all Effect combinators apply to nodes directly, and services flow from mount through the whole app.
6
+
7
+ Streams drive every update; there is no virtual DOM. The same tree renders to HTML on the server and `hydrate()`s in place on the client, flash-free. No JSX.
6
8
 
7
9
  The docs follow the [Diátaxis](https://diataxis.fr) model. Pick your entry point by what you are trying to do:
8
10
 
9
11
  ## Start here
10
12
 
11
- **[→ Tutorial](https://weftui.dev/docs/tutorial/01-your-first-app)** a four-step guided path from a static component to a server-rendered, error-handled app. Start here if you are new to Weft:
13
+ **[→ Tutorial](https://weftui.dev/docs/tutorial/01-your-first-app)**: a four-step guided path from a static component to a server-rendered, error-handled app. Start here if you are new to Weft:
12
14
 
13
- 1. [Your First App](https://weftui.dev/docs/tutorial/01-your-first-app) `h` and `mount`
14
- 2. [Reactivity](https://weftui.dev/docs/tutorial/02-reactivity) `SubscriptionRef` and streams
15
- 3. [Services and Async](https://weftui.dev/docs/tutorial/03-services-and-async) handlers, services, async loading
16
- 4. [Errors and Server Rendering](https://weftui.dev/docs/tutorial/04-errors-and-server) boundaries and SSR
15
+ 1. [Your First App](https://weftui.dev/docs/tutorial/01-your-first-app): `h` and `WeftApp`
16
+ 2. [Reactivity](https://weftui.dev/docs/tutorial/02-reactivity): `SubscriptionRef` and streams
17
+ 3. [Services and Async](https://weftui.dev/docs/tutorial/03-services-and-async): handlers, services, async loading
18
+ 4. [Errors and Server Rendering](https://weftui.dev/docs/tutorial/04-errors-and-server): boundaries and SSR
17
19
 
18
20
  ## The four quadrants
19
21
 
20
- | | |
21
- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
22
- | **[Tutorial](https://weftui.dev/docs/tutorial/01-your-first-app)** | Learning-oriented. One guided path, start to finish. |
23
- | **[How-to guides](https://weftui.dev/docs/how-to/author-components)** | Task-oriented. Author components, render on the server, load data with rpc, add routing plus recipes for forms, async data, keyed lists, reactive styles, refs, and lazy routing. |
24
- | **[Explanation](https://weftui.dev/docs/explanation/rendering-model)** | Understanding-oriented. The rendering model, the combinator API, reactive primitives, boundaries, and services & context. |
25
- | **[Reference](https://weftui.dev/docs/reference/core)** | Information-oriented. Full API: [`@weftui/core`](https://weftui.dev/docs/reference/core), [`@weftui/dom`](https://weftui.dev/docs/reference/dom), [`@weftui/router`](https://weftui.dev/docs/reference/router). |
22
+ | | |
23
+ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | **[Tutorial](https://weftui.dev/docs/tutorial/01-your-first-app)** | Learning-oriented. One guided path, start to finish. |
25
+ | **[How-to guides](https://weftui.dev/docs/how-to/author-components)** | Task-oriented. Author components, render on the server, load data with rpc, add routing. Plus recipes for forms, async data, keyed lists, reactive styles, refs, and lazy routing. |
26
+ | **[Explanation](https://weftui.dev/docs/explanation/rendering-model)** | Understanding-oriented. The rendering model, the combinator API, reactive primitives, boundaries, and services & context. |
27
+ | **[Reference](https://weftui.dev/docs/reference/core)** | Information-oriented. Full API: [`@weftui/core`](https://weftui.dev/docs/reference/core), [`@weftui/dom`](https://weftui.dev/docs/reference/dom), [`@weftui/router`](https://weftui.dev/docs/reference/router). |
26
28
 
27
- New to the model itself? Read [The Rendering Model](https://weftui.dev/docs/explanation/rendering-model) why there is no virtual DOM, and what "streams are the weft" means.
29
+ New to the model itself? Read [The Rendering Model](https://weftui.dev/docs/explanation/rendering-model): why there is no virtual DOM, and what "streams are the weft" means.
28
30
 
29
31
  ## Packages
30
32
 
31
33
  Three published packages make up Weft's public API, plus one build-time plugin:
32
34
 
33
- - **`@weftui/core`** element builders (`h`), components, sources/streams, and boundaries. Start here.
34
- - **`@weftui/dom`** the renderer: `./client` (`mount`/`hydrate`) and `./server` (`renderToString*`).
35
- - **`@weftui/router`** universal nested routing, `Router.lazy`, and the rpc seam.
36
- - **`@weftui/vite`** a build-time Vite plugin (tooling, not a runtime API).
35
+ - **`@weftui/core`**: element builders (`h`), components, sources/streams, and boundaries. Start here.
36
+ - **`@weftui/dom`**: the renderer, with `./client` (`WeftApp.mount`/`WeftApp.hydrate`) and `./server` (`renderToString*`) entry points.
37
+ - **`@weftui/router`**: universal nested routing, `Router.lazy`, and the rpc seam.
38
+ - **`@weftui/vite`**: a build-time Vite plugin (tooling, not a runtime API).
37
39
 
38
- `@weftui/base` is an internal, currently-empty stub it has no public primitives; ignore it.
40
+ `@weftui/base` is an internal, currently-empty stub with no public primitives. Ignore it.
39
41
 
40
42
  ## Examples
41
43
 
@@ -2,7 +2,7 @@
2
2
  title: "@weftui/core"
3
3
  order: 1
4
4
  section: reference
5
- description: Full API surface for @weftui/core element builders, components, sources, streams, and boundaries.
5
+ description: "Full API surface for @weftui/core: element builders, components, sources, streams, and boundaries."
6
6
  ---
7
7
 
8
8
  # @weftui/core API Reference
@@ -52,7 +52,7 @@ Use when a component needs to return multiple sibling elements.
52
52
 
53
53
  ### `Component`
54
54
 
55
- Namespace exposing two factories for reusable components with caller-propagating reactive prop types: `Component.gen` (generator body) and `Component.make` (plain-function body). Both return a callable that is generic over the caller's specific `props`/`children`, so reactive prop values and reactive children contribute their `E`/`R` at the call site.
55
+ Namespace exposing two factories for reusable components with caller-propagating reactive prop types: `Component.gen` (generator body) and `Component.make` (plain-function body). Both return a callable that is generic over the caller's specific `props`/`children`. Reactive prop values and reactive children therefore contribute their `E`/`R` at the call site.
56
56
 
57
57
  ```typescript
58
58
  import { Component } from "@weftui/core";
@@ -69,7 +69,7 @@ Component.make<BaseProps, C>(
69
69
  ): /* same call signature as above */;
70
70
  ```
71
71
 
72
- **`Children`** the optional `children` argument may be either form:
72
+ **`Children`**: the optional `children` argument may be either form:
73
73
 
74
74
  ```typescript
75
75
  type Component.Children<Input = never> =
@@ -79,7 +79,7 @@ type Component.Children<Input = never> =
79
79
 
80
80
  For function-children, `ChildrenE`/`ChildrenR` are extracted from the function's `ReturnType`, not from the function itself. The component's body invokes the function with whatever input it chooses.
81
81
 
82
- **Example `Component.gen`**:
82
+ **Example: `Component.gen`**
83
83
 
84
84
  ```typescript
85
85
  interface TextFieldProps {
@@ -96,7 +96,7 @@ const TextField = Component.gen(function* (props: TextFieldProps) {
96
96
  });
97
97
  ```
98
98
 
99
- **Example `Component.make` with function-children**:
99
+ **Example: `Component.make` with function-children**
100
100
 
101
101
  ```typescript
102
102
  const Labeled = Component.make(
@@ -108,17 +108,17 @@ Labeled({ label: "Name" }, (label) => [h.label(label), h.input()]);
108
108
  ```
109
109
 
110
110
  > For rendering a reactive collection, reach for the built-in [`List.each`](#listeach)
111
- > rather than mapping items by hand it reconciles by key across emissions instead of
111
+ > rather than mapping items by hand. It reconciles by key across emissions instead of
112
112
  > rebuilding the region.
113
113
 
114
114
  ### `Boundary` namespace
115
115
 
116
- Variants for intercepting rendering-path errors in a subtree, plus `Boundary.suspend` for async fallbacks and `Boundary.rpc` for rpc-backed server data. Each returns a descriptor that the renderer processes via the same `{ type, props }` branch. The catch variants share the same call shape props first, children array second.
116
+ Variants for intercepting rendering-path errors in a subtree, plus `Boundary.suspend` for async fallbacks and `Boundary.rpc` for rpc-backed server data. Each returns a descriptor that the renderer processes via the same `{ type, props }` branch. The catch variants share the same call shape: props first, children array second.
117
117
 
118
118
  **What is caught:**
119
119
 
120
- - Construction-time failures the Effect phase of building child nodes
121
- - Post-mount stream failures streams driving children or prop values that fail after mount
120
+ - Construction-time failures: the Effect phase of building child nodes
121
+ - Post-mount stream failures: streams driving children or prop values that fail after mount
122
122
 
123
123
  **What is NOT caught:** event handler errors (they run in detached fibers outside the render path).
124
124
 
@@ -144,7 +144,9 @@ Boundary.suspend(
144
144
 
145
145
  #### `Boundary.rpc`
146
146
 
147
- A universal server/client render boundary backed by one `Rpc` from the app's merged `RpcGroup` ([`effect/unstable/rpc`](https://github.com/Effect-TS/effect)). The rpc **`_tag`** is the boundary's stable identity and its **payload schema** the typed input; the handler lives in the server-only rpc Layer (`group.toLayer(...)`), which the client never imports tree-shaking does the client/server split structurally. Unlike the catch variants it takes a `render` function — not a children array — and that `render` receives a reactive [`Resource`](#resourcea), not a bare value.
147
+ A universal server/client render boundary backed by one `Rpc` from the app's merged `RpcGroup` ([`effect/unstable/rpc`](https://github.com/Effect-TS/effect)). The rpc **`_tag`** is the boundary's stable identity and its **payload schema** the typed input. The handler lives in the server-only rpc Layer (`group.toLayer(...)`), which the client never imports: tree-shaking does the client/server split structurally.
148
+
149
+ Unlike the catch variants it takes a `render` function, not a children array. That `render` receives a reactive [`Resource`](#resourcea), not a bare value.
148
150
 
149
151
  ```typescript
150
152
  Boundary.rpc<R extends Rpc.Any, C extends Node<any, any>>(
@@ -157,27 +159,27 @@ Boundary.rpc<R extends Rpc.Any, C extends Node<any, any>>(
157
159
 
158
160
  The boundary resolves the rpc through the ambient [`AppRpcClientTag`](#apprpcclienttag) seam, provided by `@weftui/router` (`RouterServer` on the server, `RouterLive` on the client). It has four lifecycles:
159
161
 
160
- - **SSR:** the server resolves the rpc in-process (over the handler Layer), `successSchema`-encodes the result inline as `<script type="application/json">` at the region cursor, then renders `render(seededResource)` to HTML in place.
161
- - **Hydrate:** `hydrate` reads the inline payload positionally, `successSchema`-decodes it, seeds the `Resource`, and adopts the DOM it **never re-calls the rpc** (replay, not refetch).
162
- - **Refetch:** `resource.refetch` calls the rpc again over the network (`POST /_eui/rpc`) and patches the subtree in place (**stale-on-error** a failed refetch leaves the previous value intact).
162
+ - **SSR:** the server resolves the rpc in-process (over the handler Layer), `successSchema`-encodes the result inline as `<script type="application/json">` at the region cursor. It then renders `render(seededResource)` to HTML in place.
163
+ - **Hydrate:** `hydrate` reads the inline payload positionally, `successSchema`-decodes it, seeds the `Resource`, and adopts the DOM. It **never re-calls the rpc** (replay, not refetch).
164
+ - **Refetch:** `resource.refetch` calls the rpc again over the network (`POST /_eui/rpc`) and patches the subtree in place. **Stale-on-error**: a failed refetch leaves the previous value intact.
163
165
  - **Client-first mount:** SPA-navigating into a boundary with **no** SSR payload renders `options.fallback`, forks the rpc call, and swaps in `render(resource)` once it resolves.
164
166
 
165
- **Channel algebra:** the output `E` is `render`'s error union plus the rpc's typed `Rpc.Error<R>` (`never` for an rpc with no `error` schema). The output `R` is **exactly** `render`'s `R`, untouched there is no `provide`/`RServer` to discharge (the handler lives in the rpc Layer) and **no `Exclude`** is applied. A server-only tag accidentally referenced in `render` therefore stays in `R`, where `hydrate`'s `AssertNoServerOnly` rejects it. Brand such services with [`ServerTag`](#servertag).
167
+ **Channel algebra:** the output `E` is `render`'s error union plus the rpc's typed `Rpc.Error<R>` (`never` for an rpc with no `error` schema). The output `R` is **exactly** `render`'s `R`, untouched. There is no `provide`/`RServer` to discharge (the handler lives in the rpc Layer) and **no `Exclude`** is applied. A server-only tag accidentally referenced in `render` therefore stays in `R`, where `hydrate`'s `AssertNoServerOnly` rejects it. Brand such services with [`ServerTag`](#servertag).
166
168
 
167
- **Typed-failure replay:** a resolved rpc **error** on the SSR pass is `errorSchema`-encoded and relocated to the nearest enclosing failure `Boundary`, then replayed on the client (decoded and re-raised, reproducing the same fallback DOM never retried). A transport **defect**, or an rpc with no `error` schema, is not replayed; it propagates.
169
+ **Typed-failure replay:** a resolved rpc **error** on the SSR pass is `errorSchema`-encoded and relocated to the nearest enclosing failure `Boundary`. It is then replayed on the client: decoded and re-raised, reproducing the same fallback DOM (never retried). A transport **defect**, or an rpc with no `error` schema, is not replayed; it propagates.
168
170
 
169
171
  > **Not yet covered:** streamed success (`Rpc.make(..., { stream: true })`) and mutations are on the roadmap, not this pass.
170
172
 
171
173
  ##### `Resource<A>`
172
174
 
173
- The reactive handle `render` receives (`A = Rpc.Success<R>`). After hydrate the region is live: `value` is seeded with the SSR payload and the client can `refetch` the same data on demand, patching the rendered subtree in place.
175
+ The reactive handle `render` receives (`A = Rpc.Success<R>`). After hydrate the region is live: `value` is seeded with the SSR payload. The client can `refetch` the same data on demand, patching the rendered subtree in place.
174
176
 
175
- | Field | Type | Meaning |
176
- | --------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
177
- | `value` | `Subscribable.Subscribable<A>` | Current data. Seeded with the SSR `data` (await-first, emits immediately, so SSR HTML and adopted DOM are byte-identical no fallback flash). A successful refetch pushes the new value. |
178
- | `refetch` | `Effect.Effect<void>` | Re-resolves the rpc over the network with a fresh `payload()` and sets `value`. Client only a no-op on the server. |
179
- | `pending` | `Subscribable.Subscribable<boolean>` | `true` while a refetch is in flight (`false` on the server / before any refetch). |
180
- | `error` | `Subscribable.Subscribable<Option<unknown>>` | `Some` with the last refetch error, else `None`. A failed refetch is stale-on-error it does **not** unmount or raise into a failure `Boundary`. |
177
+ | Field | Type | Meaning |
178
+ | --------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
179
+ | `value` | `Subscribable.Subscribable<A>` | Current data. Seeded with the SSR `data` (await-first, emits immediately, so SSR HTML and adopted DOM are byte-identical, no fallback flash). A successful refetch pushes the new value. |
180
+ | `refetch` | `Effect.Effect<void>` | Re-resolves the rpc over the network with a fresh `payload()` and sets `value`. Client only, a no-op on the server. |
181
+ | `pending` | `Subscribable.Subscribable<boolean>` | `true` while a refetch is in flight (`false` on the server / before any refetch). |
182
+ | `error` | `Subscribable.Subscribable<Option<unknown>>` | `Some` with the last refetch error, else `None`. A failed refetch is stale-on-error: it does **not** unmount or raise into a failure `Boundary`. |
181
183
 
182
184
  ##### `RpcOptions`
183
185
 
@@ -206,7 +208,9 @@ class AppRpcClientTag extends Context.Service<AppRpcClientTag, AppRpcClient>()(
206
208
  ) {}
207
209
  ```
208
210
 
209
- The ambient, package-neutral seam the renderer resolves a `Boundary.rpc` through a **flat, untyped** caller `(tag, payload) => Effect<success>`. It lets `@weftui/dom` resolve a boundary without importing `effect/unstable/rpc` or `@weftui/router`. `@weftui/router` provides it: a **network** `RpcClient` (POST `/_eui/rpc`) in the browser, an **in-process** client over the handler Layer on the server. `call` returns the already-decoded success; the renderer owns `successSchema`/`errorSchema` decoding of the inline SSR payload only. Both `AppRpcClientTag` and the `AppRpcClient` type are re-exported from `@weftui/core`. Absent in a router-less mount, where a `Boundary.rpc` resolves to a descriptive "needs router/rpc" error (not a defect).
211
+ The ambient, package-neutral seam the renderer resolves a `Boundary.rpc` through: a **flat, untyped** caller `(tag, payload) => Effect<success>`. It lets `@weftui/dom` resolve a boundary without importing `effect/unstable/rpc` or `@weftui/router`. `@weftui/router` provides it: a **network** `RpcClient` (POST `/_eui/rpc`) in the browser, an **in-process** client over the handler Layer on the server.
212
+
213
+ `call` returns the already-decoded success; the renderer owns `successSchema`/`errorSchema` decoding of the inline SSR payload only. Both `AppRpcClientTag` and the `AppRpcClient` type are re-exported from `@weftui/core`. Absent in a router-less mount, where a `Boundary.rpc` resolves to a descriptive "needs router/rpc" error (not a defect).
210
214
 
211
215
  See the [rpc data boundaries guide](https://weftui.dev/docs/how-to/load-data-with-rpc) and [examples/router-ssr](https://github.com/stefvw93/weft/tree/main/examples/router-ssr).
212
216
 
@@ -267,7 +271,9 @@ Boundary.catchTags<C, Handlers>(
267
271
 
268
272
  #### `Boundary.catchFilter`
269
273
 
270
- Conditionally catches using a `Filter`, run on each typed failure: a `Result.succeed` (pass) recovers via `fallback`, receiving the possibly-narrowed pass value; a `Result.fail` re-raises the error (its `Fail` channel `X` is preserved in the output `E`, since the boundary may not handle any given error). Takes the `Filter` and `fallback` as **positional** arguments — no wrapping props object. Mirrors Effect 4's `Effect.catchFilter` (renamed from `catchSome`, which took an `Option`-returning function in v3).
274
+ Conditionally catches using a `Filter`, run on each typed failure. A `Result.succeed` (pass) recovers via `fallback`, receiving the possibly-narrowed pass value. A `Result.fail` re-raises the error; its `Fail` channel `X` is preserved in the output `E`, since the boundary may not handle any given error.
275
+
276
+ Takes the `Filter` and `fallback` as **positional** arguments (no wrapping props object). Mirrors Effect 4's `Effect.catchFilter` (renamed from `catchSome`, which took an `Option`-returning function in v3).
271
277
 
272
278
  ```typescript
273
279
  Boundary.catchFilter<C, EB, X, FE, FR>(
@@ -288,8 +294,6 @@ Boundary.catchFilter(
288
294
  );
289
295
  ```
290
296
 
291
- The children's `E` is narrowed to the filter's `Fail` channel `X` in the output, because the boundary may or may not handle any given error.
292
-
293
297
  #### `Boundary.catchIf`
294
298
 
295
299
  A predicate gates the fallback. `false` re-raises.
@@ -308,7 +312,7 @@ Boundary.catchIf<C, FE, FR>(
308
312
 
309
313
  When a boundary's `match` returns `null` (unmatched error), the error propagates to the nearest **parent** `Boundary` via `BoundaryContext`. If there is no parent boundary, the error fails the enclosing mount.
310
314
 
311
- Inner boundaries shadow outer ones for their subtree the innermost boundary is always tried first.
315
+ Inner boundaries shadow outer ones for their subtree: the innermost boundary is always tried first.
312
316
 
313
317
  ```typescript
314
318
  // Inner catches FooError; BarError propagates to outer
@@ -323,7 +327,7 @@ Boundary.catch({ fallback: (e) => h.div(`Outer: ${e.message}`) }, [
323
327
 
324
328
  ## ServerTag
325
329
 
326
- A `Context.Service` key whose identifier is branded server-only. Use it exactly like `Context.Service` for services that must only ever be provided on the server e.g. a database handle read inside an rpc handler Layer. The brand also guards [`Boundary.rpc`](#boundaryrpc): a server-only tag accidentally referenced in `render` stays in the requirement channel, where `hydrate`'s `AssertNoServerOnly` rejects it at compile time.
330
+ A `Context.Service` key whose identifier is branded server-only. Use it exactly like `Context.Service` for services that must only ever be provided on the server (e.g. a database handle read inside an rpc handler Layer). The brand also guards [`Boundary.rpc`](#boundaryrpc): a server-only tag accidentally referenced in `render` stays in the requirement channel, where `hydrate`'s `AssertNoServerOnly` rejects it at compile time.
327
331
 
328
332
  ```typescript
329
333
  import { ServerTag } from "@weftui/core";
@@ -336,8 +340,8 @@ class Database extends ServerTag("Database")<
336
340
  ```
337
341
 
338
342
  - The server-only brand rides along in the requirement channel `R` of any effect that uses the tag.
339
- - An rpc handler Layer (`group.toLayer(...)`) discharges it on the server, where it is provided; it never enters a [`Boundary.rpc`](#boundaryrpc)'s output `R`, since `render` only reads the decoded result.
340
- - If a branded tag ever reaches client code referenced in `render` and surviving into `hydrate`'s requirement channel `AssertNoServerOnly` resolves `R` to a compile-error sentinel (`ServerOnlyLeak`) at the `hydrate` call site, rather than failing silently at runtime.
343
+ - An rpc handler Layer (`group.toLayer(...)`) discharges it on the server, where it is provided. It never enters a [`Boundary.rpc`](#boundaryrpc)'s output `R`, since `render` only reads the decoded result.
344
+ - If a branded tag ever reaches client code (referenced in `render` and surviving into `hydrate`'s requirement channel), `AssertNoServerOnly` resolves `R` to a compile-error sentinel (`ServerOnlyLeak`). The failure surfaces at the `hydrate` call site, not silently at runtime.
341
345
 
342
346
  `ServerOnly`, `ServerOnlyLeak`, and `AssertNoServerOnly<R>` are exported alongside `ServerTag` for advanced typing; most code only needs `ServerTag` itself.
343
347
 
@@ -347,10 +351,10 @@ class Database extends ServerTag("Database")<
347
351
 
348
352
  ### `List` namespace
349
353
 
350
- The keyed-list combinator. It is the opt-in alternative to wholesale child rebuilds: items are rendered **once per key** and reconciled across emissions, so reordering, inserting, or removing items reuses and moves existing DOM rather than rebuilding the region.
354
+ The keyed-list combinator. It is the opt-in alternative to wholesale child rebuilds: items are rendered **once per key** and reconciled across emissions. Reordering, inserting, or removing items therefore reuses and moves existing DOM rather than rebuilding the region.
351
355
 
352
356
  > **Note:** This exported `List` namespace is the built-in, key-reconciling way to
353
- > render collections prefer it over hand-rolling a component that maps items into
357
+ > render collections. Prefer it over hand-rolling a component that maps items into
354
358
  > elements.
355
359
 
356
360
  ```typescript
@@ -372,7 +376,7 @@ List.each<S extends Source.Source<Iterable<any>, any, any>, CE, CR, K>(
372
376
 
373
377
  `render` runs **once per key**; a persisted key keeps its DOM nodes and its running subscription fibers across re-emits (it is never re-invoked). The returned node's `E`/`R` are the union of the source channels and the channels of the node `render` returns.
374
378
 
375
- > **⚠️ Render-once / index-key footgun:** because `render` runs exactly once per key, reconciliation never refreshes a kept row's content refresh a row by threading a `Stream` **inside** it, not by re-running `render`. Keying by index (`by: (_, i) => i`) reuses rows positionally and will show stale content after a reorder; prefer a stable identity key (`by: (item) => item.id`).
379
+ > **⚠️ Render-once / index-key footgun:** because `render` runs exactly once per key, reconciliation never refreshes a kept row's content. Refresh a row by threading a `Stream` **inside** it, not by re-running `render`. Keying by index (`by: (_, i) => i`) reuses rows positionally and will show stale content after a reorder. Prefer a stable identity key (`by: (item) => item.id`).
376
380
 
377
381
  **`List.Options<S, K>`**
378
382
 
@@ -383,8 +387,8 @@ interface List.Options<S, K> {
383
387
  }
384
388
  ```
385
389
 
386
- - **`of`** the list source. Each emission is materialized to an array to fix order, then reconciled by key.
387
- - **`by`** projects each item to its reconciliation key. Omitted ⇒ the item itself is the key (structural for `Data`, by reference otherwise).
390
+ - **`of`**: the list source. Each emission is materialized to an array to fix order, then reconciled by key.
391
+ - **`by`**: projects each item to its reconciliation key. Omitted ⇒ the item itself is the key (structural for `Data`, by reference otherwise).
388
392
 
389
393
  #### `List.Error<N>` and `List.Context<N>`
390
394
 
@@ -435,7 +439,7 @@ Normalization rules:
435
439
  - **`Effect`** → memoized via `Effect.cached`; `changes` emits the resolved value once
436
440
  - **`Stream`** → forks a scoped pump fiber that drains into a `SubscriptionRef`; `get` awaits the first emission
437
441
 
438
- The pump fiber is tied to the enclosing scope via `Effect.forkScoped` it terminates when the scope closes.
442
+ The pump fiber is tied to the enclosing scope via `Effect.forkScoped`. It terminates when the scope closes.
439
443
 
440
444
  ### `NoPropValue`
441
445
 
@@ -458,7 +462,7 @@ type PropsE<P> = { [K in keyof P]: P[K] extends Stream.Stream<any, infer E, any>
458
462
  type PropsR<P> = { [K in keyof P]: P[K] extends Stream.Stream<any, any, infer R> ? R : ... }[keyof P]
459
463
  ```
460
464
 
461
- These are used internally by `h` and `Component` to accumulate channels from props. You generally don't need to reference them directly unless building utilities over the combinator API.
465
+ These are used internally by `h` and `Component` to accumulate channels from props. Reference them directly only when building utilities over the combinator API.
462
466
 
463
467
  ---
464
468
 
@@ -466,7 +470,7 @@ These are used internally by `h` and `Component` to accumulate channels from pro
466
470
 
467
471
  ### `FRAGMENT`
468
472
 
469
- Internal brand used to mark fragment nodes. Not intended for direct use use `h.fragment` instead.
473
+ Internal brand used to mark fragment nodes. Not intended for direct use; use `h.fragment` instead.
470
474
 
471
475
  ---
472
476
 
@@ -502,6 +506,6 @@ isSubscribable(value: unknown): value is Subscribable<unknown, unknown, unknown>
502
506
 
503
507
  ## See also
504
508
 
505
- - [The Combinator API](https://weftui.dev/docs/explanation/combinator-api) · [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives) · [Boundaries and Suspense](https://weftui.dev/docs/explanation/boundaries-and-suspense) the concepts behind this surface
506
- - [Author Components](https://weftui.dev/docs/how-to/author-components) · [Render Keyed Lists](https://weftui.dev/docs/how-to/render-keyed-lists) task guides that use it
509
+ - [The Combinator API](https://weftui.dev/docs/explanation/combinator-api) · [Reactive Primitives](https://weftui.dev/docs/explanation/reactive-primitives) · [Boundaries and Suspense](https://weftui.dev/docs/explanation/boundaries-and-suspense): the concepts behind this surface
510
+ - [Author Components](https://weftui.dev/docs/how-to/author-components) · [Render Keyed Lists](https://weftui.dev/docs/how-to/render-keyed-lists): task guides that use it
507
511
  - [`@weftui/dom` reference](https://weftui.dev/docs/reference/dom) · [`@weftui/router` reference](https://weftui.dev/docs/reference/router)