# Solidjs V2

> Answer SolidJS 2.0 API and prerelease-behavior questions, and write or edit SolidJS 2.0 code (solid-js 2.x / next / RC). Use for questions or implementation involving components, signals/effects/batching, async data/generators/actions and Loading, stores and nested store views, lazy SSR/hydration, server functions ("use server"), experimental server components/frames, or DOM code for solid-js 2.x or @solidjs/web. Not for Solid 1.x projects and not for migrating 1.x code (see solidjs-v2-migration).

- Skill: `khmm12/solidjs-v2` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add khmm12/solidjs-v2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/khmm12/solidjs-v2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: khmm12 (https://skillmd.com/u/khmm12)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/khmm12/solidjs-v2

---


# SolidJS 2.0

Solid 2.0 is **not React** and **not Solid 1.x**. Both priors are the dominant
bug sources in generated code. When in doubt, distrust pattern-matching and
check `references/cheatsheet.md` (official, ships with the package) or the
installed typings in `node_modules`.

## Step 0 — confirm this is actually a v2 project

Check before applying anything below:

- `package.json`: `solid-js` major is `2` (e.g. `2.0.0-rc.x`), and/or
  `@solidjs/web` is a dependency.
- `tsconfig.json`: `"jsxImportSource": "@solidjs/web"`.

If `solid-js` is `1.x` (imports like `solid-js/web`, `solid-js/store`), **stop —
these rules do not apply**; that's a Solid 1.x project. If the task is to
convert it, use the `solidjs-v2-migration` skill instead.

Prereleases drift: when docs and the installed package disagree, trust the typings in
`node_modules` (`solid-js`, `@solidjs/web`, `@solidjs/signals`).

## The ten rules that prevent most bugs

1. **Reads lag writes.** Updates apply on the next microtask:
   `setCount(1); count()` still returns `0`. Synchronous point: `flush()`.
   `batch()` does not exist.
2. **`createEffect` takes two functions** — `(compute, apply, options?)`.
   Compute tracks and returns a value; apply does side effects (untracked) and
   may return a cleanup. The 1.x single-callback form throws. `on()`,
   `createComputed`, initial-value args: all gone.
3. **Never write signals/stores or invoke an action inside a reactive scope**
   (memo, compute, component body) — throws in dev. Define actions there if
   useful, but invoke/write from event handlers, effect callbacks, actions, or
   `onSettled`. `untrack()` suppresses read tracking but does not exempt writes.
   Derive instead of writing back.
4. **No top-level reactive reads in component bodies** and no destructured
   props — warns, value goes stale. Read via `props.x` inside JSX / memos /
   effect computes; `untrack(() => ...)` for deliberate one-shots.
5. **Props are values, not accessors.** Call site: `<X v={count()} />`, never
   `<X v={count} />`. Child: `props.v`, never `function X({ v })`. This stays
   reactive — the compiler turns `v={count()}` into `{ get v() { return count() } }`,
   so reading `props.v` in the child re-runs `count()` in the child's tracking
   scope. Passing the accessor (`v={count}` + `props.v()`) to "keep reactivity" is
   a misconception: props have **always** been getters in Solid (1.x and 2.0
   alike — value-passing didn't change), so it's unnecessary and just forces
   every consumer to call a function.
6. **Async is just a computation**: `const user = createMemo(() => fetchUser(id()))`
   — no `createResource`. Wrap consumers in `<Loading fallback={...}>`;
   errors go to `<Errored>`. In-flight-change indicators: `isPending(() => user())`
   — fires for changed inputs and `affects()` declarations; a bare `refresh()`
   is normally quiet. `await refresh(source)` waits for the settled re-ask;
   `until(predicate)` waits for a truthy live-source acknowledgement.
7. **Store setters take a draft**: `setStore(s => { s.a.b = 1; })` (produce is
   the default). Store APIs (`createStore`, `reconcile`, `snapshot`…) are
   exported from `solid-js` — `solid-js/store` does not exist.
8. **List rendering is `For` with keying modes** — `<Index>` is gone. Callback
   shapes differ per mode (see references); `keyed={false}` gives
   `(itemAccessor, plainIndex)`. Fixed-count rendering: `<Repeat>`.
9. **Lifecycle**: `onSettled(() => { ...; return cleanup; })` replaces
   `onMount`/`onCleanup` for component-level setup-and-teardown. It's a leaf
   owner — no primitives or `onCleanup` inside.
10. **Imports moved**: `@solidjs/web` for `render`/`hydrate`/`Portal`/`Dynamic`
    (not `solid-js/web`); `jsxImportSource: "@solidjs/web"`; DOM attributes are
    lowercase (`tabindex`); `class` takes object/array forms (`classList` is
    gone); directives are `ref={factory(opts)}` (`use:` is gone).

## Reference routing

Read the file matching the task before writing code in that area:

| Task touches | Read |
|---|---|
| Quick API lookup, import list, full 1.x→2.0 footgun list | `references/cheatsheet.md` (official) |
| Signals, memos, split/render effects and paint timing, `createReaction`, batching/flush, lifecycle, ownership, dev diagnostics | `references/reactivity.md` |
| Data fetching, loading values, async iterator completion, Loading/Errored, isPending/latest/resolve/awaitable refresh/until, action call scope/errors, optimistic UI | `references/async-and-actions.md` |
| createStore, reconcile, projections, nested store-view structural tracking, compiler patch-driver boundary, snapshot/deep, merge/omit, storePath | `references/stores.md` |
| For/Repeat/Show/Switch/Reveal, dynamic/lazy components, lazy SSR/hydration identity, class/attributes/events/refs/directives, render entries | `references/control-flow-and-dom.md` |
| tsconfig, JSX types, import paths, Context typing, test setup | `references/typescript-setup.md` |
| Composed patterns: SWR query, optimistic mutations, selection projections, global state, demand-driven resources | `references/patterns.md` |
| Naming a primitive/composable (`create*` vs `use*`), cross-cutting conventions | `references/conventions.md` |
| `"use server"` directive, module/function wrappers, server-function addressing/invoke/live, respond/redirect/reload, GET/withMeta, fetch/prepareRequest, named single-flight, no-JS, getRequestEvent | `references/server-functions.md` |
| Experimental server components, frames, client slots/state preservation, `installServerComponents`, `serverFunctions: { components: true }` | `references/server-components.md` |

## Failure modes

- **App renders nothing / mount seems stuck** → pending async outside a
  `Loading` boundary defers the root mount; check the console for
  `ASYNC_OUTSIDE_LOADING_BOUNDARY`.
- **Dev throws/warns with a diagnostic code** (`REACTIVE_WRITE_IN_OWNED_SCOPE`,
  `STRICT_READ_UNTRACKED`, …) → table of codes and fixes at the bottom of
  `references/reactivity.md`. Fix the cause; never silence with `ownedWrite`
  for app state.
- **Test asserts stale values** → missing `flush()` after writes, or reactive
  code created without an owner (`createRoot` in tests).
- **An API from docs/examples doesn't exist** → prereleases drift; verify against
  installed typings and prefer them over any doc, including these references.

