# State Street — guide for coding agents You are about to write code in **State Street**, a small reactive single-page-app framework. It is **not** React/Vue/Svelte/Angular and does not share their APIs or mental model. Read this first; most bugs come from applying habits from those frameworks. (Human-facing docs: https://joshabracks.github.io/State-Street/ — this file is the distilled, agent-oriented version.) ## Do NOT do these (they do not exist / will break) - **No JSX/TSX.** Components return **HTML strings**, written with template literals. There is no `.jsx`/`.tsx`, no Babel/JSX transform, no `React.createElement`/`h()`. - **No virtual DOM, no hooks.** No `useState`, `useEffect`, `useRef`, `useMemo`, no `key` prop, no `setState`. State is one plain object. - **No `className`, no `onClick`.** Use the HTML attribute `class`, and the directive `:click=method()` (see Events). React-style `onClick={...}` does nothing. - **No router.** No `react-router`, ``, ``, `history.pushState` routing. Navigation is hash + a `view` key (see Navigation). - **No Context/Redux/stores, no `ReactDOM.render`, no lifecycle methods** (`componentDidMount`…). - **No `style={{...}}` objects.** Use a normal `style="..."` string, or CSS classes. ## Mental model (read this) - A **component is a plain function** that takes a context object and **returns an HTML string**: `({ state }) => \`

\${state.data.count}

\``. - There is **one reactive state object**, `state.data` (a Proxy). Assigning to a top-level key marks it dirty and schedules a re-render on the next animation frame. - Re-rendering is **dependency-gated**: a component re-runs only when a `state.data` key it *read during its last render* changes. `state.data` is a Proxy with two traps — the **set trap** (writing a key marks that top-level key dirty) and the **get trap** (reading a key records it as a dependency of the component running). **The read is the subscription:** any `state.data.` touched anywhere in the body subscribes the component to that key — in a conditional, a computed local, an existence check, or inside a `${}` — *even if the value never appears in the output*. Reading more keys = more re-renders; read only what you need. - Components are referenced as **tags** in templates: ``. State Street finds them by name in the `components` registry and renders each as its own dep-tracked subtree, delimited by HTML comment markers (there is no wrapper element). ## Hello world ```js import { State } from "@state-street/state-street"; const template = `

{{title}}

`; const data = { title: "Hello State Street", count: 0 }; const components = { Counter: ({ state }) => `

Count: ${state.data.count}

`, }; const methods = { increment: ({ state }) => { state.data.count += 1; }, reset: ({ state }) => { state.data.count = 0; }, }; // Mounts into and starts a requestAnimationFrame render loop. new State(template, data, components, methods); ``` ## Templates: State Bindings vs `${}` - `{{ path }}` — a **State Binding**, State Street's official term for this syntax. A State Binding is a reactive reference to a `state.data` value: it renders as a standalone text/attribute node and updates that node **in place** when the bound key changes, **without re-running the component**. Use it in the root `template` and inside component strings for live values: `

{{title}}

`. - `${ ... }` — **plain JavaScript** template-literal interpolation, evaluated **once** when the function runs (it is not special to State Street). If it reads `state.data.x`, that read subscribes the *whole* component, so changing `x` re-runs and rebuilds it. > **Best practice: prefer State Bindings over `${}` for reactive values.** `{{count}}` updates just > that node in place; `${state.data.count}` re-runs the whole component whenever `count` changes. > Reserve `${}` for control flow (loops, conditionals), derived/computed strings, and composition — > things a binding can't express. Component tags accept attributes, passed to the component as named props with **type coercion**: ```js `` // component receives: { state, src, size: "lg", count: 478, active: true, label: } ``` `478` → number, `true`/`false` → boolean, `{{name}}` → the value of `state.data.name`, quoted → string. ## Events / directives Bind DOM events with `:event=method(args)`. **The parentheses are required** — `:click=submit` is parsed as a plain attribute and does nothing; `:click=submit()` works. ```js `` `` `` `` // :input, :change, :keydown, etc. ``` The method receives `{ state, event, ...args }` — `event` is the DOM event; args are coerced like attributes: ```js const methods = { setCount: ({ state, n }) => { state.data.count = n; }, //