Hedronite Lesson · Polyglot-Dev / Rust · Tue 2026-10-13 · ABC Phase C

Leptos primary contrast · Name the Update Graph · Signal Read Is a Subscription · Effect Is the Other Half

Same button. Different graphs. Contrast, not a product pick.

Lesson Class: Asr (ABC Frontend · Phase C Bridges · Leptos primary-contrast)
Focus: Name the Update Graph · Signal Read Is a Subscription · Effect Is the Other Half
Done-criteria: signals vs useState named; claim ledger; lab sketches; no Next/Bevy; register --lane asr
Grounding: book.leptos.dev Introduction + reactivity (fetched 2026-10-13) · React managing-state as twin
Note: Leptos CSR hello not compiled (time box; crate absent from local registry)
Update Graph
React invalidates a component function; Leptos invalidates signal subscribers.
Subscription
A signal read inside a reactive scope subscribes. Outside, it does not.
Boundary
Phase A stays Vite+React. Dioxus is the next C-day. Next/Vercel/Bevy stay out.
Name the graph before you port the button.

Why this fire

Tuesday is Phase C on the ABC Frontend seat. The Phase C cursor opens at a Leptos primary-contrast lab: one Rust UI crate held next to the Vite + React path that Phase A will teach on Sun/Wed. This is contrast, not a product pick. Leptos stays a lab language beside the TypeScript home; the register lane remains Asr frontend.

Sun 2026-10-11 and Mon 2026-10-12 Asr fires were HELD (the lab Mac offline). Those Phase A and Phase B rows still return on the next matching weekdays. Today ships the first C-day only.

Frame

The Leptos book places the framework next to Solid and Sycamore for fine-grained reactivity, and notes kinship with React, Svelte, Yew, and Dioxus. That kinship is the lesson: same DOM job, different update graph.

React (Vite SPA path):

Leptos (CSR / WASM path, author claims from the book):

If you already think in React, the trap is assuming "component re-ran." In Leptos the unit of update is the reactive edge, not the function body.

Technique — Name the Update Graph

Before you port a counter or a form, write one sentence that names what invalidates:

PathInvalidation unitTypical write
ReactComponent function (then VDOM diff)setCount(c => c+1)
LeptosSubscribers of a signal`set_count.update(\c\*c += 1)`

If you cannot name the unit, you will paste React habits into a signal graph and wonder why a parent "re-render" never happened.

Technique — Signal Read Is a Subscription

In React, reading count inside JSX is just a value from the last render. In Leptos, reading a signal inside a reactive scope subscribes that scope. Move the read outside the reactive scope and the subscription dies. That is the Solid-shaped rule, not the React-shaped one.

Sketch (Leptos-shaped; not compiled this fire — see Lab):

// Illustrative shape from the Leptos book family (signals + view macro).
// Labels: author-claim API shape from book.leptos.dev / docs.rs/leptos — verify on your pinned crate version before pasting into a Cargo project.

use leptos::prelude::*;

#[component]
fn Counter() -> impl IntoView {
    let (count, set_count) = signal(0);

    view! {
        <button on:click=move |_| *set_count.write() += 1>
            "Click: " {count}
        </button>
    }
}

React twin for the same job:

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Click: {count}
    </button>
  );
}

Same button. Different graphs. The React write re-enters Counter. The Leptos write patches the text node bound to count without re-executing the whole component builder as a React developer expects.

Technique — Effect Is the Other Half

The reactivity chapter in the Leptos book states the system is built to run expensive side effects (DOM work, network) as infrequently as possible when reactive values change. Signals are the cells; effects are the scheduled work. React's useEffect is a cousin with a different dependency array ritual. In Leptos, dependencies are collected from signal reads inside the effect body (author claim: fine-grained tracking), not declared as a second array you can desync.

When you reach for useEffect(() => {...}, [count]) in React, ask: is this a reactive effect that should re-run on signal read, or a one-shot setup? That question ports. The API names do not.

Where Vite + React still win for Asr Phase A

Phase A Foundations (Sun/Wed) stay on Vite 8 + React + Bun-as-install + Oxlint/Oxfmt, with TanStack Start / SPA + Hono as the product shell. Reasons that stay true for this seat even after a Leptos lab:

  1. Hire and paste density. Most frontend examples you will steal this month are React or Solid-in-JS, not Rust view macros.
  2. Tooling on the ABC arm. Bun install/CLI, Oxlint/Oxfmt, Tailwind, TanStack — the Phase A bucket is JS-toolchain shaped.
  3. Deno is the C-day runtime shell (Bun stays install/CLI only). A Leptos CSR app still needs wasm-pack / trunk / cargo; that is a second toolchain tax on a C-day that already has Deno + TS glue for WASM host work later in the cursor.

Leptos buys you: Rust types at the UI boundary, fine-grained updates, and a straight line into later WASM-host TS glue (still Phase C, later rows). It does not buy you a free pass out of Phase A's Vite camp.

Boundary against Dioxus / Yew / Next

Lab (the lab Mac)

Goal: feel the contrast without standing up a full trunk serve this fire.

Ran

  1. Confirmed rustc 1.99.0 / cargo 1.99.0 on the lab Mac.
  2. Wrote lab/contrast_notes.md and lab/react_twin.tsx (TypeScript sketch only; no bun/Vite app scaffolded).
  3. Wrote lab/leptos_counter_sketch.rs as an illustrative fragment. Not compiled. No leptos crate in the local cargo registry at fire time; a first cargo add leptos + wasm target would blow the time box.

Not run (labeled)

Re-run path when the time box is loose: cargo new --lib leptos-asr-c1 && cd leptos-asr-c1, add leptos with CSR features per current docs.rs, add wasm target, render the counter sketch, then diff React DevTools highlight vs Leptos signal update (browser performance panel). Record your own numbers; do not quote blog speed claims.

Claim ledger

ClaimSourceStatus
Leptos is most similar to Solid and Sycamore; kinship with React/Svelte/Yew/Dioxusbook.leptos.dev Introduction (fetched 2026-10-13)quoted as author claim
Reactive system runs expensive side effects as infrequently as possiblebook.leptos.dev/reactivity/ (fetched 2026-10-13)quoted as author claim
Exact signal / set_count.write() API spellingdocs.rs/leptos familyunverified at fire — sketch only; confirm against the crate version you pin
Leptos CSR builds are "faster than React"(none pinned)not claimed

What this buys next to Rust-core / Vite-React

Buys

Does not buy

WASM edge — where Asr C meets the TS host

Phase C later rows cover TS glue for the WASM host. This Leptos fire is the Rust side of that handshake. A CSR Leptos app compiles to wasm32-unknown-unknown, then a JS/TS bootstrap (often generated by trunk or wasm-bindgen) mounts it into a page that Vite could also own.

Two seams to name before Fri's Dioxus contrast:

  1. Ownership of the page shell. If Vite owns index.html and the Leptos bundle is an island, the React tree and the Leptos signal graph do not share a reconciler. Props cross the boundary as JSON-ish values or wasm-bindgen types, not as React children.
  2. Disposal. The 2026-09-25 Duha TypeScript lesson on using / Symbol.dispose at the WASM edge still applies: a WASM view that allocates must expose a free path. Leptos scopes dispose reactive nodes; your host still has to drop the wasm instance when the island unmounts.

Do not build that host today. Name the seams so the Deno + TS glue row has a place to land.

ROD note

Voice: Musashi for the update-graph table; Yagyu for the three named techniques; Chozanshi for the if/then on Phase A vs Leptos. Blacklist scan: no operator §2.A fillers. Body prose em-dash count: 0. One real uncertainty kept: the exact signal / write() spelling against a pinned crate version.

Close instruction

Next matching C-day (Fri 2026-10-17 unless backfill moves): if this Leptos row is accepted as shipped, advance the Phase C cursor to Dioxus contrast lab. Keep one bundle. Leave Sun Phase A and Mon Phase B backfill on their weekday returns (or the castle-wake backfill queue), not on this note.