# SolidJS Documentation: Solid 2.0 for agents
> The short form for tools with a small context budget: the rules that differ from React and Solid 1.x, the 1.x to 2.0 corrections, and one line per `solid-js` export.
Index: https://deploy-preview-1657--solid-docs-v2.netlify.app/llms.txt
Full corpus: https://deploy-preview-1657--solid-docs-v2.netlify.app/llms-full.txt
Solid is not React, and 2.0 is not 1.x. Pattern-matching from either is the
dominant bug source. Every name below is a `solid-js` export unless noted;
DOM APIs (`render`, `hydrate`, `Portal`, `dynamic`) are in `@solidjs/web`.
## Three rules
1. **Props are read, not destructured; pass values, not accessors.**
- Wrong: `function C({ name }) {}` and ``
- Right: `function C(props) { return
{props.name}
; }` and ``
2. **`createEffect` takes two functions: compute (tracks), then apply (side effects).**
- Wrong: `createEffect(() => { el.title = count(); });`
- Right: `createEffect(() => count(), v => { el.title = v; });`
3. **Stores are written through the draft.**
- Wrong: `setStore("user", "name", "B")` or `setStore(produce(s => ...))`
- Right: `setStore(s => { s.user.name = "B"; });`
## 1.x → 2.0 corrections
Nothing in the left column exists in 2.0.
| Wrong (1.x) | Right (2.0) |
| --- | --- |
| `import { createStore } from "solid-js/store"` | `import { createStore } from "solid-js"` |
| `import { render } from "solid-js/web"` | `import { render } from "@solidjs/web"` |
| `"jsxImportSource": "solid-js"` | `"jsxImportSource": "@solidjs/web"` |
| `createEffect(fn)` (one argument) | `createEffect(compute, apply)` |
| `onMount(fn)` | `onSettled(() => { setup(); return cleanup; })` |
| `createEffect(on(dep, fn))` | `createEffect(() => dep(), v => fn(v))` |
| `const [d] = createResource(id, fetchX)` | `const d = createMemo(() => fetchX(id()))` under `` |
| `d.loading` / `d.error` | `` or `isPending(() => d())` / `` |
| `refetch()` | `refresh(d)` (`affects(d); refresh(d)` to read as pending) |
| `` / `` | `` / `` |
| `` | ` ...}>` (heals automatically) |
| `` | `` |
| `batch(fn)` | not needed: updates batch per microtask; `flush()` applies now |
| `startTransition` / `useTransition` | built-in transitions: `action(...)`, `isPending`, optimistic APIs |
| `createComputed` | `createMemo`, split `createEffect`, or `createSignal(fn)` |
| `mergeProps` / `splitProps` / `unwrap` | `merge` / `omit` / `snapshot` |
| `produce`, `createMutable`, `modifyMutable` | draft setters are the default in `createStore` |
| `createSelector` | `createProjection` (or `createStore(fn)`) |
| `` | `` |
| `classList={{ a: x() }}` | `class={{ a: x() }}` or `class={["base", { a: x() }]}` |
| `use:dir={x}` / `attr:` / `on:` / `tabIndex` | `ref={dir(x)}` / plain attributes / `onClick` / `tabindex` |
| `onError` / `catchError` | `` or `createEffect(c, { effect, error })` |
| `from(source)` / `observable` | return an AsyncIterable from a computation |
Also:
- Reads lag writes: `setX(1); x()` still returns the old value until the
microtask or `flush()`. In tests, `flush()` before asserting.
- No writes inside a memo, effect compute, or component body (dev throws).
Write from event handlers, `onSettled`, or actions.
- No reactive reads at the top of a component body; read in JSX, a memo, or
`untrack`. Flow callbacks (``, ``, ``) are owners, not
tracking scopes: read their accessor arguments in JSX, not at the top.
- ``: default gives a raw item and an index accessor; `keyed={false}`
gives an item accessor and a plain number. ``: `i` is a number.
- `onClick={cond() ? a : b}` binds once; decide inside a stable handler.
## Primitives to reach for
| Primitive | Use |
| --- | --- |
| `createSignal(0)` / `createSignal(() => fetchUser(id()))` | plain signal / writable derived value, sync or async; read by calling it |
| `createMemo(() => fetchX(id()))` | read-only derived value; a Promise result is pending until it lands |
| `createProjection(async () => api.list(), [], { key: "id" })` | read-only derived store with keyed reconciliation |
| `createOptimisticStore(() => api.list(), [])` | store whose optimistic writes revert when the transition settles |
| `action(function* (x) { write(); yield api.put(x); refresh(d); })` | mutation with transition coordination; optimistic writes go inside |
| `until(() => confirmed(), { timeout })` | Promise that resolves when a predicate turns truthy; `yield` it in an action |
| `isPending(() => d())` | true while a change to `d` is in flight (input changed or `affects()` declared) |
| `latest(d)` | peek at the in-flight value during a transition |
| `refresh(d)` | re-ask the source after a server write; quiet unless preceded by `affects(d)` |
| `` `` `` `` `` | async boundary (`on` re-arms it), error boundary, sibling order, range list, keyed list |
## Pointers
- Full reference: `node_modules/solid-js/CHEATSHEET.md`; the same text is
the `solid-2.0-cheatsheet` skill in `node_modules/solid-js/skills/`.
- Diagnostics: dev builds print `[CODE] message` plus an `in › …`
owner line. The first report of each code ends with a footer naming
`node_modules/solid-js/skills/reactivity-diagnostics/SKILL.md`, one section
per code (`STRICT_READ_UNTRACKED`, `REACTIVE_WRITE_IN_OWNED_SCOPE`,
`LOADING_ON_OUTSIDE_HOLD`, …). Fix the cause; never suppress a code.
- Evidence loops: `node_modules/@solidjs/diagnostics/skills/agent-loops/SKILL.md`
(`captureArtifact`). With `@solidjs/vite-plugin`'s `diagnostics` option the
dev server answers `POST /__solid/diagnostics` (`begin`, `active`, `whyDidRun`,
`costs`, `end`).
- Lint: `eslint-plugin-solid`, config `v2`
(`import solid from "eslint-plugin-solid/configs/v2"`) or `v2-strict`.
- Docs: https://v2.solidjs.com (Solid 2.0 docs; `/llms.txt` and
`/llms-full.txt` for agents). Migration guide: `documentation/solid-2.0/MIGRATION.md`
in the repository.
## solid-js exports
One line per public export of `solid-js`; the reference pages in the index carry the signatures.
### Signals and memos
| Export | Purpose |
| --- | --- |
| `createSignal` | `[get, set]` reactive value; `createSignal(fn)` is a writable derived value |
| `createMemo` | read-only derived value; a returned Promise is pending until it lands |
| `createDeferred` | async memo that keeps its last value while a refetch is in flight |
| `untrack` | run `fn` without subscribing to the reads inside it |
| `flush` | apply queued writes now instead of on the next microtask |
| `isEqual` | default equality of signals and memos |
### Effects and lifecycle
| Export | Purpose |
| --- | --- |
| `createEffect` | `(compute, apply)`: compute tracks, apply runs effects and may return a cleanup |
| `onSettled` | run once after the current activity settles; return a cleanup (replaces `onMount`) |
| `onCleanup` | cleanup tied to a reactive run or owner disposal; for primitive authors |
| `createRenderEffect` | effect that runs during render, for DOM bindings; app code uses `createEffect` |
| `createTrackedEffect` | single-callback effect that tracks and applies in one scope; rare |
| `createReaction` | one-shot tracked callback: `track(() => dep())` runs `fn` on the next change |
### Stores
| Export | Purpose |
| --- | --- |
| `createStore` | deep reactive object; `set(draft => { ... })` writes through the draft |
| `createProjection` | read-only derived store; `{ key }` gives keyed reconciliation |
| `reconcile` | `reconcile(data, key)(draft.part)` merges new data into a store sub-tree by key |
| `deep` | track every nested level of a store (default tracking is per property) |
| `snapshot` | plain untracked copy of a store for JSON, storage and tests (replaces `unwrap`) |
| `isWrappable` | whether a value would become a store proxy |
| `merge` | merge props-like objects into one reactive proxy (replaces `mergeProps`) |
| `omit` | reactive proxy of props without the listed keys (replaces `splitProps`) |
### Async, actions and transitions
| Export | Purpose |
| --- | --- |
| `action` | generator mutation: optimistic writes, `yield` async work, `refresh`; reverts on failure |
| `createOptimistic` | signal whose writes inside an action show now and revert when it settles |
| `createOptimisticStore` | store form of `createOptimistic` |
| `isPending` | true while a change to the values read inside `fn` is in flight |
| `latest` | read the in-flight value during a transition instead of the committed one |
| `refresh` | re-run one source after a server write; quiet unless `affects` was declared |
| `affects` | mark a source or store slot as pending while in-flight work will change it |
| `until` | Promise for the first time a reactive predicate turns truthy; `yield` it in an action |
| `resolve` | Promise for the first settled value of a reactive expression (tests) |
| `NotReadyError` | thrown by a read of an async source before its first value; boundaries handle it |
| `TimeoutError` | rejection from `until` when its `timeout` passes |
| `enableExternalSource` | adapter so Solid tracks reads of another reactive system (MobX, Vue) |
### Components and context
| Export | Purpose |
| --- | --- |
| `children` | resolve `props.children` once; `.toArray()` helper |
| `flatten` | resolve nested accessors and arrays in a children tree (`children` memoizes it) |
| `createContext` | context for a subtree; `` is the provider; no default means no provider throws |
| `useContext` | read the nearest context value |
| `lazy` | code-split component; pending through `` while its chunk loads |
| `lazyModule` | cached dynamic import of a non-component module; `.preload()` and `.peek()` |
| `dynamicComponent` | component that re-resolves `fn` to a component; the factory behind `dynamic` |
| `createUniqueId` | stable id that matches between server and client (`for`, `aria-labelledby`) |
### Flow components
| Export | Purpose |
| --- | --- |
| `For` | keyed list; callback gets `(item, i())`, or `(item(), i)` with `keyed={false}` |
| `Repeat` | render `count` times; `i` is a plain number |
| `Show` | render children when `when` is truthy; a function child gets a narrowed accessor |
| `Switch` | render the first `` whose `when` is truthy |
| `Match` | one branch of `` |
| `Loading` | async boundary (replaces `Suspense`); `on={deps}` re-arms it |
| `Errored` | error boundary (replaces `ErrorBoundary`); `fallback={(err, reset) => ...}` |
| `Reveal` | reveal order of sibling `` boundaries (replaces `SuspenseList`) |
| `mapArray` | reactive array map behind ``; reuses entries for unchanged items |
| `repeat` | reactive count-based render behind `` |
### Owners and roots
| Export | Purpose |
| --- | --- |
| `createRoot` | owned reactive scope with `dispose()`; tests and library entry points |
| `getOwner` | current owner, to capture across an async gap in library code |
| `runWithOwner` | run `fn` under an owner; `runWithOwner(null, fn)` detaches a root |
| `getObserver` | the currently tracking computation, or `null` |
| `isDisposed` | whether an owner has been disposed, to ignore late work |
### Hydration (advanced)
| Export | Purpose |
| --- | --- |
| `NoHydration` | opt a subtree out of client hydration |
| `Hydration` | opt a subtree back in under `` |
| `isHydrating` | whether code runs while claiming server-rendered DOM |
| `isHydratable` | whether the calling owner sits where hydration applies |
| `onHydrationEnd` | run once after the root pass and every streamed boundary have resumed |
### Dev and observability
| Export | Purpose |
| --- | --- |
| `DEV` | dev tier: devtools hooks, graph traversal, diagnostics reporting; dev builds only |
| `OBSERVE` | observe tier: records channel, diagnostics channel, attribution slot |
| `configureClientErrors` | hook called once per error when a boundary renders its fallback |