# 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; }, //