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.
- Props are read, not destructured; pass values, not accessors.
- Wrong:
function C({ name }) {}and<C value={count} /> - Right:
function C(props) { return <p>{props.name}</p>; }and<C value={count()} />
- Wrong:
createEffecttakes two functions: compute (tracks), then apply (side effects).- Wrong:
createEffect(() => { el.title = count(); }); - Right:
createEffect(() => count(), v => { el.title = v; });
- Wrong:
- Stores are written through the draft.
- Wrong:
setStore("user", "name", "B")orsetStore(produce(s => ...)) - Right:
setStore(s => { s.user.name = "B"; });
- Wrong:
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 <Loading> |
d.loading / d.error |
<Loading> or isPending(() => d()) / <Errored> |
refetch() |
refresh(d) (affects(d); refresh(d) to read as pending) |
<Suspense> / <SuspenseList> |
<Loading> / <Reveal> |
<ErrorBoundary> |
<Errored fallback={(err, reset) => ...}> (heals automatically) |
<Index each={xs}> |
<For each={xs} keyed={false}> |
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)) |
<Ctx.Provider value={v}> |
<Ctx value={v}> |
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 |
<Errored> 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 orflush(). 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 (<For>,<Show>,<Match>) are owners, not tracking scopes: read their accessor arguments in JSX, not at the top. <For>: default gives a raw item and an index accessor;keyed={false}gives an item accessor and a plain number.<Repeat>:iis a number.onClick={cond() ? a : b}binds once; decide inside a stable handler.
| 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) |
<Loading on={id()}> <Errored> <Reveal> <Repeat> <For> |
async boundary (on re-arms it), error boundary, sibling order, range list, keyed list |
- Full reference:
node_modules/solid-js/CHEATSHEET.md; the same text is thesolid-2.0-cheatsheetskill innode_modules/solid-js/skills/. - Diagnostics: dev builds print
[CODE] messageplus anin <App> › …owner line. The first report of each code ends with a footer namingnode_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'sdiagnosticsoption the dev server answersPOST /__solid/diagnostics(begin,active,whyDidRun,costs,end). - Lint:
eslint-plugin-solid, configv2(import solid from "eslint-plugin-solid/configs/v2") orv2-strict. - Docs: https://v2.solidjs.com (Solid 2.0 docs;
/llms.txtand/llms-full.txtfor agents). Migration guide:documentation/solid-2.0/MIGRATION.mdin the repository.