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.
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):
- State lives in component closures (
useState, props, context). - A state write schedules a re-render of that component function (and children, unless memoized).
- The reconciler diffs VDOM and patches the real DOM.
Leptos (CSR / WASM path, author claims from the book):
- State lives in signals — reactive cells the runtime tracks.
- A signal write notifies only the effects and views that read it.
- Rendering is an expensive effect the reactive system runs as seldom as it can.
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:
| Path | Invalidation unit | Typical write | ||
|---|---|---|---|---|
| React | Component function (then VDOM diff) | setCount(c => c+1) | ||
| Leptos | Subscribers 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:
- Hire and paste density. Most frontend examples you will steal this month are React or Solid-in-JS, not Rust view macros.
- Tooling on the ABC arm. Bun install/CLI, Oxlint/Oxfmt, Tailwind, TanStack — the Phase A bucket is JS-toolchain shaped.
- 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
- Dioxus is the next Phase C contrast lab (Fri or the next Tue if unfinished). Do not collapse Leptos and Dioxus into one bundle.
- Yew is kinship only; not on the cursor.
- Next / Turbopack / SWC / Vercel and Bevy are out forever at Asr.
Lab (the lab Mac)
Goal: feel the contrast without standing up a full trunk serve this fire.
Ran
- Confirmed
rustc 1.99.0/cargo 1.99.0on the lab Mac. - Wrote
lab/contrast_notes.mdandlab/react_twin.tsx(TypeScript sketch only; nobun/Vite app scaffolded). - Wrote
lab/leptos_counter_sketch.rsas an illustrative fragment. Not compiled. Noleptoscrate in the local cargo registry at fire time; a firstcargo add leptos+ wasm target would blow the time box.
Not run (labeled)
rustup target add wasm32-unknown-unknowncargo install trunk/wasm-pack- Any
trunk serveor Leptos CSR hello-world build - Deno shell (not installed on the lab Mac at fire)
- Fetch stub against today's Duha API (Duha 10-13 was HOLD; no API bundle)
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
| Claim | Source | Status |
|---|---|---|
| Leptos is most similar to Solid and Sycamore; kinship with React/Svelte/Yew/Dioxus | book.leptos.dev Introduction (fetched 2026-10-13) | quoted as author claim |
| Reactive system runs expensive side effects as infrequently as possible | book.leptos.dev/reactivity/ (fetched 2026-10-13) | quoted as author claim |
Exact signal / set_count.write() API spelling | docs.rs/leptos family | unverified 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
- A named alternative to VDOM re-render thinking when the UI is already a Rust crate (Lapis/Facet-adjacent later Phase B GPUI work still separate).
- Practice reading signal graphs before Phase C's WASM-host TS glue row.
- A concrete refuse list: do not bring Next; do not skip Phase A Vite camp because a Rust UI demo felt clever.
Does not buy
- Adoption as the Hedronite product frontend. This seat is learn/contrast.
- A replacement for Duha's Rust depth (TRPL / tooling). Asr C is UI-shaped, Duha stays systems-shaped.
- Permission to author Nix or Bend at this clock (Dhuhr Ops).
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:
- Ownership of the page shell. If Vite owns
index.htmland 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 orwasm-bindgentypes, not as React children. - Disposal. The 2026-09-25 Duha TypeScript lesson on
using/Symbol.disposeat 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.