# What Framework — full agent guide
> What is a signal-based reactive web framework built for AI agents. Components run once — no re-renders, no virtual DOM. The compiler lowers JSX to fine-grained DOM operations. A live MCP server (29 tools) lets agents inspect a running app, and core runtime errors are structured with a code, fix, and example. This file inlines the essentials an agent needs to write, debug, and ship correct What code.
---
## Mental model
- **Signals are functions.** `const count = signal(0, 'count')`. Read with `count()`. Write with `count(5)` or `count(c => c + 1)`. `count.set(...)` is the same write, and `count.peek()` reads without subscribing.
- **Components run once.** The component function body executes a single time. It never re-runs on state change. Reactivity comes from signals creating fine-grained DOM effects, not from re-rendering.
- **`computed()`** is lazy, cached derived state: `const doubled = computed(() => count() * 2)`.
- **`effect()`** auto-tracks the signals it reads. No dependency array. Return a function to clean up.
- **`batch()`** groups writes so effects flush once.
- Import everything from `'what-framework'`.
---
## Quickstart
```bash
npm create what@latest my-app
cd my-app
npm install
npm run dev
```
Open `http://localhost:5173`. `create-what` wires up Vite and the compiler, so you rarely touch bundler config. Bun works too: `bun create what@latest my-app && bun run dev`.
Minimal component:
```jsx
import { signal, computed, mount } from 'what-framework';
function Counter() {
const count = signal(0, 'count');
const doubled = computed(() => count() * 2);
return (
Count: {count()}
Doubled: {doubled()}
);
}
mount(, '#app'); // returns a dispose function
```
Signal reads inside JSX children and attributes are auto-wrapped by the compiler — you do not write `{() => count()}` manually.
Manual setup without the scaffolder:
```js
// vite.config.js
import { defineConfig } from 'vite';
import what from 'what-compiler/vite';
export default defineConfig({ plugins: [what()] });
```
---
## Signals API
```js
import { signal, computed, effect, batch, onMount, onCleanup, mount } from 'what-framework';
// Create state. The second arg is an optional debug name (recommended — it
// makes the signal traceable in DevTools and MCP).
const count = signal(0, 'count');
count(); // read -> 0 (subscribes the current effect)
count(5); // write -> sets to 5
count(c => c + 1); // updater write -> increments
count.set(10); // same as count(10)
count.peek(); // read WITHOUT subscribing
// Derived state — lazy and cached.
const doubled = computed(() => count() * 2);
// Side effects — auto-track reads, run on change. Return a cleanup function.
effect(() => {
const id = setInterval(() => count(c => c + 1), 1000);
return () => clearInterval(id);
});
// Batch multiple writes so effects run once at the end.
batch(() => { count(0); /* ...other writes... */ });
// Lifecycle.
onMount(() => { /* runs after first mount; good for browser-only APIs */ });
onCleanup(() => { /* runs on dispose */ });
```
Lists: use `.map()` with a `key` prop. The compiler lowers it to keyed reconciliation.
```jsx
{() => todos().map(t =>
{t.title}
)}
```
Immutable updates for objects/arrays — never mutate in place:
```js
items(prev => [...prev, newItem]); // add
items(prev => prev.filter(i => i.id !== targetId)); // remove
items(prev => prev.map(i => i.id === id ? { ...i, done: true } : i)); // update one
```
---
## Common errors and fixes
What's core runtime emits structured `WhatError` objects that serialize to JSON
with a `code`, `message`, `suggestion`, and `codeExample`. The codes below are
the ones the runtime detects and reports in dev mode.
### ERR_INFINITE_EFFECT — effect cycle
An effect writes to a signal it also reads, creating a loop (capped at 25 flush iterations).
```js
// Bad — reads and writes count in the same effect:
effect(() => { count(count() + 1); });
// Good — read without subscribing:
import { untrack } from 'what-framework';
effect(() => { count(untrack(count) + 1); });
```
### ERR_MISSING_SIGNAL_READ — signal used without calling it
Renders `[Function]` instead of the value.
```jsx
{count} // Bad — renders "[Function]"
{count()} // Good — call the signal
```
### ERR_SIGNAL_WRITE_IN_RENDER — write during render
The component body should read signals, not write them. Move writes into handlers, effects, or `onMount`.
```jsx
// Bad:
function Counter() { count(count() + 1); return {count()}; }
// Good:
function Counter() { return ; }
```
### ERR_UNSAFE_INNERHTML — raw innerHTML
Raw HTML must be opted into with the `__html` marker.
```jsx
// Bad — XSS risk, refused
// Good — explicit opt-in
```
### ERR_HYDRATION_MISMATCH — server/client HTML differ
Avoid reading browser-only APIs (`window`, `localStorage`) during the initial render; use `onMount` for client-only values.
### ERR_ORPHAN_EFFECT / ERR_MISSING_KEY
Create effects inside components or a `createRoot()` so they are cleaned up; give list items a stable `key`.
Tip for agents: the MCP tool `what_fix({ errorCode })` returns the diagnosis, fix, and example for any code offline — use it as your first move when you hit a What error.
---
## MCP DevTools usage
What ships an MCP server (`what-devtools-mcp`) that bridges an agent to a running app over the Model Context Protocol. It exposes 29 tools.
Wire it into your MCP client:
```json
{ "mcpServers": { "what-framework": { "command": "npx", "args": ["what-devtools-mcp"] } } }
```
Enable live inspection by adding the Vite plugin:
```js
import what from 'what-compiler/vite';
import whatDevToolsMCP from 'what-devtools-mcp/vite-plugin';
export default defineConfig({
plugins: [what(), whatDevToolsMCP({ port: 9229 })],
});
```
First five minutes — run in order:
1. `what_connection_status` — am I connected? how big is the app?
2. `what_diagnose` — one-call health check (errors + perf + reactivity).
3. `what_page_map` — visual structure and accessibility.
4. `what_components({ filter })` → `what_explain({ componentId })` — deep-dive a leaf component.
5. `what_signals({ filter: "...", named_only: true })` — check key state (always filter; never dump all).
Useful workflows:
- **Trace reactivity:** `what_signals({ filter })` → `what_dependency_graph({ signalId, direction: "downstream" })`.
- **Before/after a change:** `what_diff_snapshot({ action: "save" })` → make the change → `what_diff_snapshot({ action: "diff" })`.
- **Test a fix live:** `what_set_signal({ signalId, value })` then re-check with `what_diff_snapshot`.
- **Validate code offline:** `what_lint({ code })`, `what_scaffold({ type, name })`, `what_fix({ errorCode })` work without a browser.
Notes for agents:
- Component and signal IDs are ephemeral — re-query `what_components` after any `what_set_signal` that may remount the tree.
- `what_eval` is disabled by default (security); enable only with `--unsafe-eval` / `WHAT_UNSAFE_EVAL=1`.
- Module-scope signals are not attributed to any component; use `what_signals` directly rather than per-component counts.
---
## React compatibility
`what-react` runs many React ecosystem libraries inside What components. It is a
secondary feature — prefer native What APIs for new code. The tested library
matrix in REACT-COMPAT.md is the source of truth.
```bash
npm install what-react
```
---
## More
- Repository: https://github.com/CelsianJs/what-framework
- Docs site: https://whatfw.com/docs
- Agent patterns: https://github.com/CelsianJs/what-framework/blob/main/docs/AGENT-PATTERNS.md
- MCP DevTools reference: https://github.com/CelsianJs/what-framework/blob/main/docs/MCP-DEVTOOLS.md
- Compatibility matrix: https://github.com/CelsianJs/what-framework/blob/main/REACT-COMPAT.md