# Coming from Solid
You'll feel at home — Zoijs uses the same **fine-grained, no-Virtual-DOM** reactivity model as Solid (signals, value-gated computeds, "setup runs once"). The big difference: **Zoijs needs no compiler/JSX build step.**
## Concept map
| Solid | Zoijs |
|---|---|
| `createSignal(0)` → `[count, setCount]` | `createState(0)` → `count.get()` / `count.set()` |
| `createMemo(() => ...)` | `computed(() => ...)` |
| `createEffect(() => ...)` | bindings are effects; a public `effect` isn't exposed yet |
| `{i => ...}` | `each(() => items.get(), i => i.id, i => html\`...\`)` |
| JSX (compiled) | `html\`...\`` tagged template (runtime, no build) |
| `onClick={fn}` | `onclick=${fn}` |
| `onCleanup(fn)` | automatic via [ownership](../concepts/cleanup.md); public hook on the roadmap |
## The key difference: no compiler
Solid compiles JSX into precise DOM operations at build time. Zoijs does the equivalent at runtime with a small template scanner — so there's **no build step**, at the cost of a one-time parse per template (cached).
```jsx
// Solid (JSX, compiled)
function Counter() {
const [count, setCount] = createSignal(0);
return ;
}
```
```js
// Zoijs (no build)
function Counter() {
const count = createState(0);
return html``;
}
```
## Things you'll recognize
- **Setup runs once.** No re-render, no stale closures.
- **Value-gated computeds.** A `computed` whose result is unchanged doesn't wake downstream — same as Solid memos.
- **Keyed `each`.** Reuses/moves nodes; preserves DOM state.
## Things that differ
- **Signals are `get()/set()` objects**, not call/set-pair functions. `count()` becomes `count.get()`.
- **Reads in templates need `() =>`.** Where Solid's compiler wraps `{count()}` for you, Zoijs asks you to write `${() => count.get()}`.
- **No `createEffect`/`onCleanup` in the public API yet** — cleanup is automatic via ownership; these may be exposed later.