1.7 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| stack-moving-to-react | Astrolabe's UI stack is React + Zustand (migrated from Preact + signals at M0) |
|
The UI stack is React + Zustand (user decision, 2026-06-03, executed at M0 before feature
work). It started as Preact + @preact/signals; a brief intermediate step migrated to React +
@preact/signals-react, but the final call is React + Zustand — signals were dropped.
Why React: the Preact pain was React-ecosystem friction (real-React-only libraries not
cooperating with preact/compat) — not the signals model. Real React removes that whole
class of problem and makes borrowing from the reference vega-editor-reference-clone (a
React app) port directly.
Why Zustand over keeping signals: signals were never the problem, but switching framework
was the one cheap moment (M0, ~nothing implemented) to also pick the lowest-future-migration-risk
state lib. Zustand is idiomatic React, has first-class outside-React access
(getState/setState/subscribe) that fits the "logic lives in core/services, not components"
architecture, and carries no build-time transform. The signals→Zustand cost was only rewriting
unimplemented docs.
How to apply: Stores are create<State>() modules exporting a useXStore hook (state +
actions in one object); components read via useXStore(selector) (+ useShallow for object
selections); non-component code uses getState()/setState()/subscribe(); derive in selectors,
never store derived fields. See docs/architecture/01-state-and-stores.md. The repo-wide
Preact/signals→React/Zustand doc sweep is done — no stale "Preact"/"signals" wording should
remain except where it describes the sibling project Syto, Vega's own signals, or plain English.