solid-js 2.0.0-beta.7 → 2.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHEATSHEET.md ADDED
@@ -0,0 +1,562 @@
1
+ # Solid 2.0 — Cheatsheet
2
+
3
+ One-page reference for Solid 2.0. Every API exists in `solid-js` unless noted. DOM APIs are in `@solidjs/web`.
4
+
5
+ > **AI codegen warning.** Solid is **not React**, and 2.0 is **not 1.x**. Both priors are dominant bug sources here — distrust pattern-matching from either. The bottom of this file lists 2.0-specific corrections; read them before generating code.
6
+
7
+ ---
8
+
9
+ ## Imports
10
+
11
+ ```ts
12
+ import {
13
+ createSignal, createMemo, createEffect, createRoot,
14
+ For, Show, Switch, Match, Loading, Errored, Repeat, Reveal,
15
+ createStore, createProjection, snapshot, reconcile,
16
+ merge, omit,
17
+ action, createOptimistic, createOptimisticStore,
18
+ isPending, latest, refresh,
19
+ untrack, flush, onSettled,
20
+ createContext, useContext, children, lazy, createUniqueId,
21
+ } from "solid-js";
22
+
23
+ import { render, hydrate, Portal, Dynamic, dynamic } from "@solidjs/web";
24
+ ```
25
+
26
+ Old subpaths are gone:
27
+ `solid-js/web` → `@solidjs/web`. `solid-js/store` → `solid-js`. `solid-js/h` → `@solidjs/h`. `solid-js/html` → `@solidjs/html`. `solid-js/universal` → `@solidjs/universal`.
28
+
29
+ ---
30
+
31
+ ## Signals & memos
32
+
33
+ ```ts
34
+ // Plain signal
35
+ const [count, setCount] = createSignal(0);
36
+ count(); // read (call it!)
37
+ setCount(1); // queues; read returns last committed until flush
38
+ setCount(c => c + 1); // updater form
39
+
40
+ // Readonly derived
41
+ const doubled = createMemo(() => count() * 2);
42
+ doubled();
43
+
44
+ // Writable derived ("writable memo")
45
+ const [value, setValue] = createSignal(() => props.initial);
46
+
47
+ // Options
48
+ createSignal(0, { ownedWrite: true }); // allow writes from inside owned scope
49
+ createSignal(0, { unobserved: () => cleanup() });// fires when no subscribers
50
+ createMemo(fn, { lazy: true }); // defer first compute until read; autodispose when unobserved
51
+ createMemo(fn, { equals: (a, b) => a.id === b.id });
52
+ ```
53
+
54
+ **Reads update only after flush.** `setX(v); x()` returns the *previous* value until the next microtask or `flush()`.
55
+
56
+ ```ts
57
+ setCount(1);
58
+ count(); // still 0
59
+ flush();
60
+ count(); // 1
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Effects
66
+
67
+ ```ts
68
+ // Two-arg form is the only form. Compute tracks; apply runs side effects.
69
+ createEffect(
70
+ () => count(), // compute (tracks)
71
+ (value, prev) => { // apply (untracked)
72
+ el.title = value;
73
+ return () => { /* cleanup */ }; // optional cleanup
74
+ }
75
+ );
76
+
77
+ // With error handling
78
+ createEffect(
79
+ () => fetchData(id()),
80
+ {
81
+ effect: data => render(data),
82
+ error: (err, cleanup) => console.error(err)
83
+ }
84
+ );
85
+
86
+ // Run on next change only (skip initial)
87
+ createEffect(() => count(), v => log(v), { defer: true });
88
+
89
+ // Schedule once after the current activity settles — the canonical
90
+ // "do this once and clean it up on dispose" primitive (replaces 1.x
91
+ // onMount + onCleanup for component-level lifecycle).
92
+ onSettled(() => {
93
+ const id = setInterval(tick, 1000);
94
+ return () => clearInterval(id);
95
+ });
96
+ ```
97
+
98
+ `onSettled` works in component bodies (after first reactive settle) **and** in event handlers (defer work until the triggered transition settles). For component-level setup-and-teardown, **use `onSettled` with a returned cleanup, not `onCleanup` directly** — `onCleanup` is reserved for reactive cleanup inside computations (see Advanced).
99
+
100
+ ---
101
+
102
+ ## Reactive control utilities
103
+
104
+ ```ts
105
+ untrack(() => count()); // read without subscribing
106
+ flush(); // drain queued updates synchronously
107
+ isEqual(a, b); // default equality
108
+ createRoot(dispose => { /* ... */ }); // owned by parent unless detached
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Stores
114
+
115
+ ```ts
116
+ const [store, setStore] = createStore({ user: { name: "A" }, list: [] });
117
+
118
+ // Draft-first setter (canonical)
119
+ setStore(s => {
120
+ s.user.name = "B";
121
+ s.list.push("x");
122
+ });
123
+
124
+ // Return a value when mutation is awkward (filter/remove most often).
125
+ // Arrays replace by index + length; objects shallow-diff at the top level.
126
+ // No keyed reconciliation here — for that, use createProjection / createStore(fn).
127
+ setStore(s => s.list.filter(x => x !== "x"));
128
+ setStore(s => ({ ...s, list: [] }));
129
+
130
+ // Reconcile new data into a sub-tree (preserve identity)
131
+ setStore(s => { reconcile(serverTodos, "id")(s.todos); });
132
+
133
+ // Plain (non-reactive) snapshot
134
+ JSON.stringify(snapshot(store));
135
+
136
+ // Derived stores (mirror signal/memo split)
137
+ const items = createProjection(async () => api.list(), [], { key: "id" }); // readonly
138
+ const [cache, setCache] = createStore(draft => { draft.x = compute(); }, { x: 0 }); // writable
139
+ ```
140
+
141
+ `undefined` is a real value in `merge` / setters — it overrides, not "skip".
142
+
143
+ ---
144
+
145
+ ## Props
146
+
147
+ **Props are reactive values, not accessors.** Two rules, one underlying model — and together the most common AI-generated bug class in Solid.
148
+
149
+ ```jsx
150
+ const [count, setCount] = createSignal(0);
151
+
152
+ // 1. At the call site: pass the VALUE. Call accessors at the JSX boundary.
153
+ <Counter value={count()} /> // ✅
154
+ <Counter value={count} /> // ❌ child receives a function, not a number
155
+
156
+ // 2. In the child: read via `props.x`. The *property access* is what tracks.
157
+ function Counter(props) {
158
+ return <div>{props.value}</div>; // ✅ re-reads on each render
159
+ }
160
+ function Counter({ value }) { // ❌ destructure unwraps once, reactivity is dead
161
+ return <div>{value}</div>;
162
+ }
163
+ ```
164
+
165
+ The rules are two sides of the same boundary: the parent collapses its accessors to values when handing off; the JSX runtime re-wraps `props` so that `props.value` re-reads on each access. Skip step 1 and the child gets a function; skip step 2 and the child reads once.
166
+
167
+ If you genuinely need to forward a getter (rare — render props, lazy slots), pass `getValue={() => count()}` and document it. Default to values.
168
+
169
+ ### Helpers
170
+
171
+ ```ts
172
+ const merged = merge(defaults, props, overrides); // replaces mergeProps
173
+ const rest = omit(props, "class", "style"); // replaces splitProps
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Async
179
+
180
+ ```ts
181
+ // Async is just "any computation that returns a Promise / AsyncIterable"
182
+ const user = createMemo(() => fetchUser(id()));
183
+ // Reading user() suspends until ready; wrap in <Loading>.
184
+
185
+ // "Refreshing…" indicator (false during initial Loading)
186
+ isPending(() => user());
187
+
188
+ // Peek at the in-flight value during a transition
189
+ latest(id);
190
+
191
+ // Force recompute of a derived read after a server write
192
+ refresh(user);
193
+ refresh(() => query.user(id()));
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Actions & optimistic
199
+
200
+ ```ts
201
+ const [todos, setOptimisticTodos] = createOptimisticStore(() => api.list(), []);
202
+
203
+ const addTodo = action(function* (todo) {
204
+ setOptimisticTodos(s => { s.push(todo); }); // optimistic write
205
+ yield api.add(todo); // async work
206
+ refresh(todos); // re-derive
207
+ });
208
+
209
+ // Optimistic signal
210
+ const [name, setName] = createOptimistic("Alice");
211
+ ```
212
+
213
+ Optimistic writes revert when the transition completes.
214
+
215
+ ---
216
+
217
+ ## Control-flow components
218
+
219
+ ```tsx
220
+ // List, keyed by identity (default)
221
+ <For each={items()}>
222
+ {(item, i) => <Row item={item()} index={i()} />}
223
+ </For>
224
+
225
+ // List, non-keyed (replaces <Index>)
226
+ <For each={items()} keyed={false}>
227
+ {(item, i) => <Row item={item()} index={i()} />}
228
+ </For>
229
+
230
+ // List with custom key
231
+ <For each={items()} keyed={t => t.id} fallback={<Empty />}>
232
+ {item => <Row todo={item()} />}
233
+ </For>
234
+
235
+ // Range / count (no diffing) — i is a plain number, not an accessor
236
+ <Repeat count={store.items.length} fallback={<Empty />}>
237
+ {i => <Row name={store.items[i].name} />}
238
+ </Repeat>
239
+
240
+ // Conditional (function child receives narrowed accessor — call it!)
241
+ <Show when={user()} fallback={<Login />}>
242
+ {u => <Profile user={u()} />}
243
+ </Show>
244
+
245
+ // Branching
246
+ <Switch fallback={<NotFound />}>
247
+ <Match when={route() === "home"}><Home /></Match>
248
+ <Match when={route() === "profile"}>{() => <Profile />}</Match>
249
+ </Switch>
250
+
251
+ // Async boundary (replaces <Suspense>)
252
+ <Loading fallback={<Spinner />} on={id()}>
253
+ <Profile />
254
+ </Loading>
255
+
256
+ // Error boundary (replaces <ErrorBoundary>)
257
+ <Errored fallback={(err, reset) => <button onClick={reset}>retry</button>}>
258
+ <Page />
259
+ </Errored>
260
+
261
+ // Coordinate sibling Loadings (replaces <SuspenseList>)
262
+ <Reveal order="sequential" /* | "together" | "natural" */ collapsed>
263
+ <Loading fallback={<S/>}><A/></Loading>
264
+ <Loading fallback={<S/>}><B/></Loading>
265
+ </Reveal>
266
+
267
+ // Dynamic component
268
+ import { Dynamic, dynamic } from "@solidjs/web";
269
+
270
+ <Dynamic component={isEditing() ? Editor : Viewer} value={value()} />
271
+
272
+ // Or factory form (stable Component reference)
273
+ const Active = dynamic(() => isEditing() ? Editor : Viewer);
274
+ return <Active value={value()} />;
275
+ ```
276
+
277
+ `<For>` non-keyed: `item` and `i` are **accessors** — call them: `item()`, `i()`.
278
+ `<Repeat>`: `i` is a **plain number**.
279
+
280
+ ---
281
+
282
+ ## Context, components, lazy
283
+
284
+ Context is for state scoped to a subtree of the component tree. **If you
285
+ want truly app-wide state, don't use Context — a module-scope signal/store
286
+ *is* a global.** That's why the default-less form requires a Provider.
287
+
288
+ ```tsx
289
+ // Default-less — the canonical form. No Provider → ContextNotFoundError.
290
+ type TodosCtx = readonly [Store<Todo[]>, TodoActions];
291
+ const TodosContext = createContext<TodosCtx>();
292
+
293
+ function App() {
294
+ return (
295
+ <TodosContext value={createTodos()}> {/* the context IS the provider */}
296
+ <TodoList />
297
+ </TodosContext>
298
+ );
299
+ }
300
+
301
+ function TodoList() {
302
+ const [todos, { addTodo }] = useContext(TodosContext); // typed as TodosCtx
303
+ // ...
304
+ }
305
+ ```
306
+
307
+ ```tsx
308
+ // Default form — only for primitive fallbacks (theme, locale, frozen config).
309
+ // Outside any Provider, useContext returns the default.
310
+ const Theme = createContext<"light" | "dark">("light");
311
+
312
+ function Page() {
313
+ const theme = useContext(Theme); // "light" | "dark"
314
+ return <div class={theme}>...</div>;
315
+ }
316
+ ```
317
+
318
+ Don't write a `useTodos`-style wrapper that re-throws on missing Provider —
319
+ the default-less form already throws, and `useContext` is typed `T` (no
320
+ `| undefined`). The wrapper is React-flavored boilerplate that no longer
321
+ earns its keep.
322
+
323
+ ```tsx
324
+ // Resolve children once
325
+ const list = children(() => props.children);
326
+ list.toArray();
327
+
328
+ // Async component
329
+ const Heavy = lazy(() => import("./Heavy"));
330
+ ```
331
+
332
+ Component types:
333
+
334
+ ```ts
335
+ Component<P> // no implicit children
336
+ VoidComponent<P> // forbids children
337
+ ParentComponent<P> // optional JSX.Element children
338
+ FlowComponent<P, C> // requires children of type C
339
+ ```
340
+
341
+ ---
342
+
343
+ ## DOM rendering
344
+
345
+ ```ts
346
+ import { render, hydrate, Portal } from "@solidjs/web";
347
+
348
+ const dispose = render(() => <App />, document.getElementById("root")!);
349
+ hydrate(() => <App />, document.getElementById("root")!);
350
+
351
+ <Portal mount={document.body}><Modal /></Portal>
352
+ ```
353
+
354
+ ### Refs and directives
355
+
356
+ ```jsx
357
+ // Element access
358
+ <button ref={el => (myButton = el)} />
359
+
360
+ // Directive factory (replaces use:)
361
+ <input ref={autofocus} />
362
+ <button ref={tooltip({ content: "Save" })} />
363
+
364
+ // Compose multiple
365
+ <button ref={[autofocus, tooltip({ content: "Save" })]} />
366
+ ```
367
+
368
+ Two-phase directive (recommended):
369
+
370
+ ```ts
371
+ function titleDirective(source) {
372
+ // Setup phase (owned): create primitives.
373
+ let el;
374
+ createEffect(source, value => { if (el) el.title = value; });
375
+ // Apply phase (unowned): DOM writes only.
376
+ return nextEl => { el = nextEl; el.title = source(); };
377
+ }
378
+ ```
379
+
380
+ ### Attributes
381
+
382
+ ```jsx
383
+ <video muted={true} /> // boolean = presence/absence
384
+ <video muted={false} />
385
+ <some-element enabled="true" /> // when platform requires the string
386
+ ```
387
+
388
+ Lowercase HTML attribute names. No `attr:` / `bool:` / `oncapture:` namespaces. Event handlers stay camelCase (`onClick`).
389
+
390
+ ### Conditional classes — always use the array/object form
391
+
392
+ ```jsx
393
+ <div class="card" /> // static string
394
+ <div class={{ active: isActive(), invalid: !valid() }} /> // object: toggle by truthiness
395
+ <div class={["card", props.class, { active: isActive() }]} /> // array: merge entries
396
+ ```
397
+
398
+ Array entries are always-on (or further nested arrays/objects). Object entries toggle by truthiness. There is no `classList` prop — the array+object form replaces it.
399
+
400
+ **Don't build class strings manually.** String concatenation, template literals, and `.join(" ")` over conditionals are the React/`classnames` reflex. Use the array+object form so conditions compose:
401
+
402
+ ```jsx
403
+ <li class={["todo", { completed: props.todo.completed, errored: !!err() }]} /> // ✅
404
+
405
+ const cls = ["todo", // ❌
406
+ props.todo.completed && "completed",
407
+ err() && "errored",
408
+ ].filter(Boolean).join(" ");
409
+ return <li class={cls} />;
410
+ ```
411
+
412
+ ---
413
+
414
+ ## SSR (server entry)
415
+
416
+ ```ts
417
+ import {
418
+ renderToString, renderToStringAsync, renderToStream,
419
+ ssr, ssrElement, ssrClassList, ssrStyle, ssrAttribute, escape,
420
+ isServer
421
+ } from "@solidjs/web";
422
+ ```
423
+
424
+ `Portal` throws on the server. `Reveal` `order="together"` and `collapsed` require streaming (`renderToStream` / `renderToStringAsync`).
425
+
426
+ ---
427
+
428
+ ## Diagnostics (dev mode)
429
+
430
+ Common dev-mode warnings/errors you may hit:
431
+
432
+ - **Top-level reactive read in component body** — read inside JSX or wrap in `untrack`/`createMemo`.
433
+ - **Write under owned scope** — move setters into event handlers / `onSettled` / `untrack`, or opt in with `{ ownedWrite: true }`.
434
+ - **Strict read untracked** — extract values in the compute phase; don't read store proxies inside the effect callback.
435
+ - **Multiple Solid instances** — single `solid-js` install required.
436
+
437
+ Each diagnostic has a code (see RFC 08 / runtime error message) — search the docs by code.
438
+
439
+ ---
440
+
441
+ ## Advanced / escape hatches
442
+
443
+ Reach for these only when the named situation applies. **If you're not sure, you don't need them** — the common-path APIs above are the answer.
444
+
445
+ ```ts
446
+ // Reactive cleanup inside a computation — runs before the next compute and
447
+ // on disposal. For component-level setup-and-teardown, use onSettled and
448
+ // return a cleanup; onCleanup is for library/primitive internals where the
449
+ // cleanup is tied to a reactive run, not a component lifecycle.
450
+ onCleanup(() => disposeReactiveResource());
451
+
452
+ // Deep tracking — only when an effect needs to react to *any* nested store change.
453
+ // Default store tracking is property-level (preferred).
454
+ createEffect(() => deep(store), snap => save(snap));
455
+
456
+ // Render-phase synchronous effect — for DOM bindings that must run during render
457
+ // (the runtime's own attribute/property bindings). For app code, use createEffect.
458
+ createRenderEffect(() => props.title, v => { el.title = v; });
459
+
460
+ // Single-callback effect that may re-run in async situations.
461
+ // Rare; prefer createEffect.
462
+ createTrackedEffect(() => log(count()));
463
+
464
+ // One-shot tracked callback (advanced reactive patterns).
465
+ const track = createReaction(() => doWork());
466
+ track(() => count());
467
+
468
+ // Detach a root from its parent (module singletons, external integrations only).
469
+ runWithOwner(null, () => { /* ... */ });
470
+
471
+ // Get the current owner. Mostly used to capture and restore an owner across an
472
+ // async boundary inside library code.
473
+ const owner = getOwner();
474
+ runWithOwner(owner, () => { /* ... */ });
475
+
476
+ // Wait for a reactive expression to settle (imperative code / tests).
477
+ const v = await resolve(() => user());
478
+
479
+ // Are we inside a refresh() cycle? Almost never needed in app code.
480
+ isRefreshing();
481
+
482
+ // Throw to signal "not ready" through the reactive graph (library authors).
483
+ throw new NotReadyError();
484
+
485
+ // 1.x-style path setter compat. Use only when migrating; draft-first is canonical.
486
+ setStore(storePath("user", "address", "city", "Paris"));
487
+ ```
488
+
489
+ ---
490
+
491
+ ## What changed from 1.x (the AI footgun list)
492
+
493
+ If your training data is 1.x, these are the corrections. **Read this before generating Solid 2.0 code.**
494
+
495
+ ### Imports moved
496
+ - `solid-js/web` → `@solidjs/web`
497
+ - `solid-js/store` → `solid-js` (store APIs moved into core)
498
+ - `solid-js/h` / `solid-js/html` / `solid-js/universal` → `@solidjs/h` / `@solidjs/html` / `@solidjs/universal`
499
+
500
+ ### Renames
501
+ | 1.x | 2.0 |
502
+ |---|---|
503
+ | `Suspense` | `Loading` |
504
+ | `SuspenseList` | `Reveal` |
505
+ | `ErrorBoundary` | `Errored` |
506
+ | `mergeProps` | `merge` |
507
+ | `splitProps` | `omit` |
508
+ | `unwrap` | `snapshot` |
509
+ | `onMount` | `onSettled` |
510
+ | `createSelector` | `createProjection` (or `createStore(fn)`) |
511
+ | `equalFn` | `isEqual` |
512
+ | `getListener` | `getObserver` |
513
+ | `Context.Provider` | `<Context value={...}>` (context value *is* the provider) |
514
+ | `classList={{...}}` | `class={{...}}` (object/array forms) |
515
+
516
+ ### Removed (with replacements)
517
+ | Removed | Use instead |
518
+ |---|---|
519
+ | `batch` | Default microtask batching; `flush()` to apply now |
520
+ | `createComputed` | `createMemo` / split `createEffect` / function-form `createSignal` |
521
+ | `createResource` | Async computations + `<Loading>` (`createMemo(() => fetchX(id()))`) |
522
+ | `startTransition`, `useTransition` | Built-in transitions; `isPending` / `<Loading>` / optimistic APIs |
523
+ | `on(...)` helper | Split effects (compute phase = explicit deps) |
524
+ | `onError` / `catchError` | `<Errored>` or effect `error` option |
525
+ | `produce` | Default — store setters are draft-first |
526
+ | `createMutable` / `modifyMutable` | `createStore` with draft setters |
527
+ | `from` / `observable` | Async iterables in computations / `createEffect` to push out |
528
+ | `Index` | `<For keyed={false}>` |
529
+ | `indexArray` | `mapArray` (handles non-keyed too) |
530
+ | `use:foo={x}` directives | `ref={foo(x)}` (or array `ref={[a, b(x)]}`) |
531
+ | `attr:` / `bool:` namespaces | Standard attribute behavior |
532
+ | `oncapture:` | `addEventListener(..., { capture: true })` |
533
+ | `resetErrorBoundaries` | Boundaries heal automatically |
534
+
535
+ ### Behavior changes
536
+ - **`createEffect` takes two arguments now**: `(compute, apply)`. The single-arg form is gone — using it is an error.
537
+ - **Setters don't update reads immediately** — values become visible after the microtask flushes (or via `flush()`).
538
+ - **No writes inside owned scope** — writing a signal/store from inside a memo, effect compute, or component body throws in dev. Move writes to event handlers, `onSettled`, or untracked blocks. Opt in narrowly with `{ ownedWrite: true }` for internal state.
539
+ - **No top-level reactive reads in component body** — reading signals/props directly at the top of a component warns. Read inside JSX, a memo, or `untrack`.
540
+ - **Props are values, not accessors** — at the call site call accessors (`<X v={count()} />`, not `<X v={count} />`). The single most common AI-generated bug.
541
+ - **Don't destructure props** — `function Comp({ name })` warns; use `props.name` to keep reactivity. (Same root cause as above; see the Props section.)
542
+ - **`<For>` non-keyed children are accessors** — `(item, i) => ...` where `item` and `i` are functions. Call them: `item()`, `i()`.
543
+ - **`<Show>` / `<Match>` function children receive narrowed accessors** — also call them.
544
+ - **Stores: setters take a draft callback** — mutate the draft in place by default. Returning a new value is shallow (array index-replace, object top-level diff); reach for it for filter/remove. Keyed reconcile is a *projection-fn* feature, not a setter feature.
545
+ - **`undefined` is a real value in `merge`** — it overrides rather than "skip this key".
546
+ - **Async lives in computations** — return a Promise/AsyncIterable from `createMemo`/`createStore(fn)`/`createProjection`. Reads suspend; wrap in `<Loading>`.
547
+ - **`Loading` is initial-only by default** — once content has rendered, revalidation keeps it visible. Use `isPending(() => x())` for "refreshing…" indicators. Use `<Loading on={key}>` to re-show fallback on key changes.
548
+ - **No `Suspense.Provider` or single error path** — async errors flow to `<Errored>` (or effect `error`); no inline `resource.error` branching.
549
+ - **`createRoot` is owned by parent by default** — disposed when parent disposes. To detach: `runWithOwner(null, fn)`.
550
+ - **Refs are functions** — `ref={el => ...}`. No `useRef`-style ref objects. Compose with arrays: `ref={[a, b]}`.
551
+ - **Boolean attributes are presence/absence** — `<video muted={false} />` removes the attribute.
552
+ - **Built-in attributes are lowercase** — `tabindex` not `tabIndex`. Event handlers stay camelCase (`onClick`).
553
+ - **In tests, `flush()` before asserting on signals** — `setCount(1); flush(); expect(count()).toBe(1)`.
554
+ - **Reactive primitives need an owner** — wrap test code in `createRoot(dispose => { ... })` or you'll leak.
555
+
556
+ ---
557
+
558
+ ## See also
559
+
560
+ - [`MIGRATION.md`](https://github.com/solidjs/solid/blob/main/documentation/solid-2.0/MIGRATION.md) — full beta-tester migration guide.
561
+ - [Solid 2.0 RFCs](https://github.com/solidjs/solid/tree/main/documentation/solid-2.0) — eight deep-dive design docs, one per subsystem.
562
+