claude-toolkit 0.1.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +126 -0
  4. package/bin/cli.ts +112 -0
  5. package/core/agents/ct-code-reviewer.md +123 -0
  6. package/core/agents/ct-github-workflow.md +137 -0
  7. package/core/commands/ct/code-quality.md +59 -0
  8. package/core/commands/ct/onboard.md +84 -0
  9. package/core/commands/ct/pr-review.md +104 -0
  10. package/core/commands/ct/pr-summary.md +59 -0
  11. package/core/commands/ct/proto-check.md +74 -0
  12. package/core/commands/ct/ticket.md +71 -0
  13. package/core/hooks/skill-eval.js +381 -0
  14. package/core/hooks/skill-eval.sh +35 -0
  15. package/core/hooks/skill-rules.schema.json +112 -0
  16. package/core/skills/systematic-debugging/SKILL.md +44 -0
  17. package/core/skills/testing-patterns/SKILL.md +52 -0
  18. package/core/skills/typescript-conventions/SKILL.md +57 -0
  19. package/core/skills/verification-before-completion/SKILL.md +42 -0
  20. package/docs/README.md +49 -0
  21. package/docs/agents/code-reviewer.md +76 -0
  22. package/docs/agents/github-workflow.md +98 -0
  23. package/docs/best-practices/solidjs/README.md +43 -0
  24. package/docs/best-practices/solidjs/anti-patterns.md +166 -0
  25. package/docs/best-practices/solidjs/component-patterns.md +131 -0
  26. package/docs/best-practices/solidjs/context-and-global-state.md +131 -0
  27. package/docs/best-practices/solidjs/control-flow.md +124 -0
  28. package/docs/best-practices/solidjs/data-fetching.md +205 -0
  29. package/docs/best-practices/solidjs/effects-and-lifecycle.md +113 -0
  30. package/docs/best-practices/solidjs/performance.md +100 -0
  31. package/docs/best-practices/solidjs/props-patterns.md +100 -0
  32. package/docs/best-practices/solidjs/reactivity-model.md +104 -0
  33. package/docs/best-practices/solidjs/signals-and-state.md +78 -0
  34. package/docs/best-practices/solidjs/stores-and-nested-state.md +111 -0
  35. package/docs/best-practices/solidjs/typescript-integration.md +186 -0
  36. package/docs/best-practices/typescript/README.md +45 -0
  37. package/docs/best-practices/typescript/any-and-unknown.md +73 -0
  38. package/docs/best-practices/typescript/deriving-vs-decoupling.md +83 -0
  39. package/docs/best-practices/typescript/discriminated-unions.md +75 -0
  40. package/docs/best-practices/typescript/enums-alternatives.md +72 -0
  41. package/docs/best-practices/typescript/essential-patterns.md +119 -0
  42. package/docs/best-practices/typescript/generics-patterns.md +105 -0
  43. package/docs/best-practices/typescript/micro-opinions.md +87 -0
  44. package/docs/best-practices/typescript/runtime-validation.md +62 -0
  45. package/docs/best-practices/typescript/satisfies-operator.md +100 -0
  46. package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +129 -0
  47. package/docs/best-practices/typescript/type-organization.md +64 -0
  48. package/docs/best-practices/typescript/type-vs-interface.md +80 -0
  49. package/docs/commands/code-quality.md +42 -0
  50. package/docs/commands/onboard.md +72 -0
  51. package/docs/commands/pr-review.md +102 -0
  52. package/docs/commands/pr-summary.md +50 -0
  53. package/docs/commands/proto-check.md +59 -0
  54. package/docs/commands/ticket.md +56 -0
  55. package/docs/skills/systematic-debugging.md +70 -0
  56. package/docs/skills/testing-patterns.md +89 -0
  57. package/docs/skills/typescript-conventions.md +137 -0
  58. package/docs/skills/verification-before-completion.md +91 -0
  59. package/docs/stacks/cloudflare-d1-kv.md +110 -0
  60. package/docs/stacks/i18n-typesafe.md +141 -0
  61. package/docs/stacks/protobuf-contracts.md +85 -0
  62. package/docs/stacks/rust-wasm-patterns.md +106 -0
  63. package/docs/stacks/solidjs-patterns.md +110 -0
  64. package/docs/stacks/vanilla-extract-patterns.md +115 -0
  65. package/package.json +58 -0
  66. package/src/generator.ts +317 -0
  67. package/src/index.ts +30 -0
  68. package/src/types.ts +85 -0
  69. package/src/utils.ts +53 -0
  70. package/stacks/cloudflare/skills/cloudflare-d1-kv/SKILL.md +84 -0
  71. package/stacks/cloudflare/stack.json +26 -0
  72. package/stacks/i18n-typesafe/skills/i18n-typesafe/SKILL.md +64 -0
  73. package/stacks/i18n-typesafe/stack.json +25 -0
  74. package/stacks/protobuf/skills/protobuf-contracts/SKILL.md +78 -0
  75. package/stacks/protobuf/stack.json +25 -0
  76. package/stacks/rust-wasm/skills/rust-wasm-patterns/SKILL.md +76 -0
  77. package/stacks/rust-wasm/stack.json +26 -0
  78. package/stacks/solidjs/skills/solidjs-patterns/SKILL.md +66 -0
  79. package/stacks/solidjs/stack.json +52 -0
  80. package/stacks/vanilla-extract/skills/vanilla-extract-patterns/SKILL.md +76 -0
  81. package/stacks/vanilla-extract/stack.json +40 -0
  82. package/templates/claude-toolkit.config.ts +34 -0
  83. package/templates/configs/biome.base.json +35 -0
  84. package/templates/configs/tsconfig.base.json +16 -0
  85. package/templates/skill-rules.base.json +98 -0
@@ -0,0 +1,205 @@
1
+ # Data Fetching
2
+
3
+ > Sources: [Fetching Data](https://docs.solidjs.com/guides/fetching-data), [createResource](https://docs.solidjs.com/reference/basic-reactivity/create-resource) — SolidJS Docs
4
+
5
+ ## createResource
6
+
7
+ The primary tool for async data in SolidJS. Wraps an async operation into a reactive signal with built-in loading, error, and state tracking.
8
+
9
+ ```typescript
10
+ const [user, { mutate, refetch }] = createResource(userId, async (id) => {
11
+ const res = await fetch(`/api/users/${id}`);
12
+ return res.json();
13
+ });
14
+ ```
15
+
16
+ - `userId` is the **source signal** — when it changes, the fetcher re-runs
17
+ - The fetcher receives the source value as its first argument
18
+ - Returns a resource signal with reactive properties
19
+
20
+ ### Resource Properties
21
+
22
+ ```typescript
23
+ user() // The resolved data (or undefined)
24
+ user.loading // Boolean — is a fetch in progress?
25
+ user.error // Error object if the fetch failed
26
+ user.state // "unresolved" | "pending" | "ready" | "refreshing" | "errored"
27
+ user.latest // Most recent successful data (persists during refetch)
28
+ ```
29
+
30
+ ## Handling States in JSX
31
+
32
+ ### With Control Flow
33
+
34
+ ```typescript
35
+ <Show when={!user.loading} fallback={<Spinner />}>
36
+ <Show when={!user.error} fallback={<Error message={user.error.message} />}>
37
+ <UserCard user={user()} />
38
+ </Show>
39
+ </Show>
40
+ ```
41
+
42
+ ### With Suspense + ErrorBoundary (recommended)
43
+
44
+ ```typescript
45
+ <ErrorBoundary fallback={(err, reset) => (
46
+ <div>
47
+ <p>Failed: {err.message}</p>
48
+ <button onClick={reset}>Retry</button>
49
+ </div>
50
+ )}>
51
+ <Suspense fallback={<Skeleton />}>
52
+ <UserCard /> {/* reads createResource internally */}
53
+ </Suspense>
54
+ </ErrorBoundary>
55
+ ```
56
+
57
+ Suspense detects resource reads within its boundary and shows the fallback until all resolve. This is the idiomatic pattern.
58
+
59
+ ## Optimistic Updates with `mutate`
60
+
61
+ Update the UI immediately, then let the server catch up:
62
+
63
+ ```typescript
64
+ const [tasks, { mutate, refetch }] = createResource(fetchTasks);
65
+
66
+ function addTask(text: string) {
67
+ // Optimistic: update UI immediately
68
+ mutate(prev => [...(prev ?? []), { id: "temp", text, completed: false }]);
69
+
70
+ // Then sync with server
71
+ postTask(text).then(() => refetch());
72
+ }
73
+ ```
74
+
75
+ `mutate` overwrites the resource value without calling the fetcher.
76
+
77
+ ## Refetching
78
+
79
+ Force a refetch independent of the source signal:
80
+
81
+ ```typescript
82
+ const [prices, { refetch }] = createResource(fetchPrices);
83
+
84
+ // Poll every 10 seconds
85
+ const interval = setInterval(refetch, 10_000);
86
+ onCleanup(() => clearInterval(interval));
87
+ ```
88
+
89
+ ## createAsync (SolidStart / Solid Router)
90
+
91
+ For SolidStart apps, `createAsync` is the recommended async primitive. It's a thin wrapper over `createResource` designed for router-integrated data loading:
92
+
93
+ ```typescript
94
+ import { createAsync } from "@solidjs/router";
95
+
96
+ const user = createAsync(() => getUser());
97
+ ```
98
+
99
+ `createAsync` is intended to become the standard async primitive in Solid 2.0.
100
+
101
+ ## Runtime Validation with Zod / Valibot
102
+
103
+ Data entering your app from APIs, forms, or `localStorage` should be validated at runtime — TypeScript types don't exist at runtime. See [TypeScript Runtime Validation](../typescript/runtime-validation.md) for when to validate.
104
+
105
+ ### Validating API Responses in `createResource`
106
+
107
+ Parse and type-narrow data inside the fetcher so the resource signal is guaranteed to hold valid data:
108
+
109
+ ```typescript
110
+ import { z } from "zod";
111
+
112
+ const UserSchema = z.object({
113
+ id: z.string(),
114
+ name: z.string(),
115
+ email: z.string().email(),
116
+ });
117
+
118
+ type User = z.infer<typeof UserSchema>;
119
+
120
+ const [user] = createResource(userId, async (id): Promise<User> => {
121
+ const res = await fetch(`/api/users/${id}`);
122
+ const json = await res.json();
123
+ return UserSchema.parse(json); // Throws on invalid shape
124
+ });
125
+ ```
126
+
127
+ `z.infer<typeof Schema>` keeps your TypeScript types and runtime validation in sync — no duplication.
128
+
129
+ ### A Reusable Typed Fetch Helper
130
+
131
+ ```typescript
132
+ const fetchTyped = async <T extends z.ZodSchema>(
133
+ url: string,
134
+ schema: T,
135
+ ): Promise<z.infer<T>> => {
136
+ const res = await fetch(url);
137
+ return schema.parse(await res.json());
138
+ };
139
+
140
+ // Usage with createResource
141
+ const [user] = createResource(userId, (id) =>
142
+ fetchTyped(`/api/users/${id}`, UserSchema)
143
+ );
144
+ ```
145
+
146
+ ### Form Validation
147
+
148
+ [@modular-forms/solid](https://modularforms.dev/solid/guides/validate-your-fields) has built-in Zod integration via the `zodForm` adapter:
149
+
150
+ ```typescript
151
+ import { createForm } from "@modular-forms/solid";
152
+ import { zodForm } from "@modular-forms/solid";
153
+
154
+ const LoginSchema = z.object({
155
+ email: z.string().min(1, "Required").email("Invalid email"),
156
+ password: z.string().min(8, "Min 8 characters"),
157
+ });
158
+
159
+ type LoginForm = z.infer<typeof LoginSchema>;
160
+
161
+ const [form, { Form, Field }] = createForm<LoginForm>({
162
+ validate: zodForm(LoginSchema),
163
+ });
164
+ ```
165
+
166
+ ### Valibot as a Lighter Alternative
167
+
168
+ [Valibot](https://valibot.dev/) provides the same validation patterns with a tree-shakeable design — ~1.4kb vs Zod's ~13kb for a typical form schema. Ryan Carniato (SolidJS creator) has endorsed it, and `@modular-forms/solid` supports it natively via `valiForm`.
169
+
170
+ ```typescript
171
+ import * as v from "valibot";
172
+
173
+ const UserSchema = v.object({
174
+ id: v.string(),
175
+ name: v.pipe(v.string(), v.nonEmpty()),
176
+ email: v.pipe(v.string(), v.email()),
177
+ });
178
+
179
+ type User = v.InferInput<typeof UserSchema>;
180
+ ```
181
+
182
+ **Choose based on your constraints:**
183
+
184
+ | Criteria | Zod | Valibot |
185
+ | --------------- | --------- | ---------- |
186
+ | Bundle size | ~13kb | ~1.4kb |
187
+ | Tree-shakeable | No | Yes |
188
+ | Ecosystem | Larger | Growing |
189
+ | API style | Chaining | Functional |
190
+ | Runtime speed | Baseline | ~2x faster |
191
+
192
+ ## Don't Fetch in Effects
193
+
194
+ ```typescript
195
+ // WRONG — no loading state, no error handling, race conditions
196
+ createEffect(async () => {
197
+ const data = await fetch(`/api/${id()}`);
198
+ setResult(await data.json());
199
+ });
200
+
201
+ // RIGHT — built-in state management, cancellation, reactivity
202
+ const [result] = createResource(id, fetchData);
203
+ ```
204
+
205
+ Effects run after render, causing a flash of empty state. Resources integrate with Suspense and provide proper loading/error states.
@@ -0,0 +1,113 @@
1
+ # Effects & Lifecycle
2
+
3
+ > Sources: [Effects](https://docs.solidjs.com/concepts/effects), [onMount](https://docs.solidjs.com/reference/lifecycle/on-mount), [onCleanup](https://docs.solidjs.com/reference/lifecycle/on-cleanup) — SolidJS Docs
4
+
5
+ ## createEffect
6
+
7
+ Runs a side effect whenever its tracked dependencies change. Dependencies are automatically detected.
8
+
9
+ ```typescript
10
+ createEffect(() => {
11
+ document.title = `${count()} items`;
12
+ });
13
+ ```
14
+
15
+ ### When to Use Effects
16
+
17
+ Effects are for **interacting with the outside world** — the DOM, third-party libraries, logging, WebSockets.
18
+
19
+ **Use effects for:**
20
+ - DOM manipulation not covered by JSX
21
+ - Third-party library integration
22
+ - WebSocket connections
23
+ - Logging / analytics
24
+
25
+ **Don't use effects for:**
26
+ - Derived values → use `createMemo` or inline derivation
27
+ - State synchronization → derive instead of sync
28
+ - Data fetching → use `createResource`
29
+
30
+ ### The #1 Anti-Pattern: Syncing State
31
+
32
+ ```typescript
33
+ // WRONG — creates glitchy intermediate states
34
+ createEffect(() => {
35
+ setDoubled(count() * 2);
36
+ });
37
+
38
+ // RIGHT — derive the value
39
+ const doubled = createMemo(() => count() * 2);
40
+ ```
41
+
42
+ When you use effects to sync state, the reactive graph must choose an execution order, which can produce inconsistent intermediate states that flash in the UI.
43
+
44
+ ## `on` — Explicit Dependencies
45
+
46
+ When you need to control which signals trigger an effect:
47
+
48
+ ```typescript
49
+ import { on } from "solid-js";
50
+
51
+ createEffect(on(userId, (id) => {
52
+ // Only runs when userId changes, not when other signals read inside change
53
+ fetchUserData(id);
54
+ }));
55
+ ```
56
+
57
+ `on` also provides the previous value:
58
+
59
+ ```typescript
60
+ createEffect(on(count, (current, previous) => {
61
+ console.log(`Changed from ${previous} to ${current}`);
62
+ }));
63
+ ```
64
+
65
+ ## onMount
66
+
67
+ Runs once after the component's initial render, when DOM elements are available. Non-reactive — it doesn't track signals.
68
+
69
+ ```typescript
70
+ onMount(() => {
71
+ // DOM is ready, refs are populated
72
+ inputRef?.focus();
73
+
74
+ // Good for one-time setup
75
+ const chart = new Chart(canvasRef);
76
+ });
77
+ ```
78
+
79
+ **Use for:** ref access, one-time DOM setup, measurements, API calls that should only happen once.
80
+
81
+ ## onCleanup
82
+
83
+ Runs when the enclosing reactive scope is disposed or re-executed. Essential for preventing memory leaks.
84
+
85
+ ```typescript
86
+ // In a component — runs when component unmounts
87
+ onCleanup(() => {
88
+ chart.destroy();
89
+ });
90
+
91
+ // In an effect — runs before each re-execution and on dispose
92
+ createEffect(() => {
93
+ const id = setInterval(() => tick(), props.interval);
94
+ onCleanup(() => clearInterval(id));
95
+ });
96
+ ```
97
+
98
+ **Always clean up:**
99
+ - Event listeners
100
+ - Timers (`setInterval`, `setTimeout`)
101
+ - WebSocket connections
102
+ - Third-party library instances
103
+ - Subscriptions
104
+
105
+ ## Lifecycle Order
106
+
107
+ 1. Component function runs (synchronous)
108
+ 2. Reactive expressions are set up (signals, memos, effects registered)
109
+ 3. DOM is created and mounted
110
+ 4. `onMount` callbacks fire
111
+ 5. Effects run (in dependency order)
112
+ 6. On signal change: effects re-run, `onCleanup` fires before each re-run
113
+ 7. On unmount: all `onCleanup` callbacks fire
@@ -0,0 +1,100 @@
1
+ # Performance
2
+
3
+ > Sources: [lazy](https://docs.solidjs.com/reference/component-apis/lazy), [batch](https://docs.solidjs.com/reference/reactive-utilities/batch), [untrack](https://docs.solidjs.com/reference/reactive-utilities/untrack) — SolidJS Docs
4
+
5
+ ## What Solid Optimizes For You
6
+
7
+ Before reaching for performance tools, understand what Solid already does:
8
+
9
+ - **No virtual DOM diffing** — updates go directly to the specific DOM nodes
10
+ - **Components run once** — no re-renders, no stale closures
11
+ - **Automatic batching in stores** — multiple `setState` calls in the same synchronous block are batched
12
+ - **Conditional dependency tracking** — branches not taken don't create subscriptions
13
+ - **Equality checks on signals** — redundant updates are ignored
14
+
15
+ Most performance problems in Solid come from **fighting the reactive model**, not from the framework itself.
16
+
17
+ ## `batch` — Group Signal Updates
18
+
19
+ When updating multiple independent signals in one synchronous operation, `batch` prevents intermediate re-renders:
20
+
21
+ ```typescript
22
+ import { batch } from "solid-js";
23
+
24
+ batch(() => {
25
+ setFirstName("John");
26
+ setLastName("Doe");
27
+ setAge(30);
28
+ });
29
+ // All effects that depend on these signals run once, not three times
30
+ ```
31
+
32
+ Without `batch`, each setter triggers its dependents immediately. With `batch`, all updates are applied, then dependents run once with the final state.
33
+
34
+ **Note:** Store `setState` calls are already batched automatically. `batch` is primarily useful for multiple `createSignal` setters.
35
+
36
+ ## `untrack` — Read Without Subscribing
37
+
38
+ Read a reactive value without creating a dependency:
39
+
40
+ ```typescript
41
+ import { untrack } from "solid-js";
42
+
43
+ createEffect(() => {
44
+ // This effect re-runs when query() changes
45
+ // But does NOT re-run when options() changes
46
+ fetchData(query(), untrack(() => options()));
47
+ });
48
+ ```
49
+
50
+ **Use for:**
51
+ - Static configuration that shouldn't trigger re-runs
52
+ - Initial values you read once
53
+ - Avoiding circular dependencies
54
+
55
+ ## `lazy` — Code Splitting
56
+
57
+ Lazy-load components to reduce initial bundle size:
58
+
59
+ ```typescript
60
+ import { lazy } from "solid-js";
61
+
62
+ const AdminPanel = lazy(() => import("./AdminPanel"));
63
+
64
+ // Renders like a normal component
65
+ // Loads the code on first render, triggers Suspense boundaries
66
+ <Suspense fallback={<Spinner />}>
67
+ <AdminPanel />
68
+ </Suspense>
69
+ ```
70
+
71
+ The component loads on first render, not on import. Combine with `<Suspense>` for loading states.
72
+
73
+ ## `<Dynamic>` — Avoid Mounting All Variants
74
+
75
+ Instead of conditionally showing/hiding multiple heavy components, use `<Dynamic>` to mount only the active one:
76
+
77
+ ```typescript
78
+ const panels = { settings: Settings, profile: Profile, billing: Billing };
79
+
80
+ <Dynamic component={panels[activePanel()]} />
81
+ ```
82
+
83
+ ## Performance Anti-Patterns
84
+
85
+ | Anti-Pattern | Why It's Slow | Fix |
86
+ |---|---|---|
87
+ | Recreating objects in signals | Triggers all subscribers on every set | Use stores for nested data |
88
+ | Using `.map()` instead of `<For>` | Recreates all DOM nodes on any change | Use `<For>` or `<Index>` |
89
+ | Effects that sync state | Creates cascading updates | Derive with `createMemo` |
90
+ | Reading signals at component top level | Captures once, never updates | Read in JSX or effects |
91
+ | Wrapping everything in effects | Unnecessary tracking overhead | Only for external side effects |
92
+
93
+ ## When to Actually Worry
94
+
95
+ Solid's reactivity is already very fast. Optimize only when you observe actual performance issues:
96
+
97
+ 1. **Lists with 1000+ items** — ensure you're using `<For>` not `.map()`
98
+ 2. **Rapid signal updates** (e.g., mouse move) — consider `batch` or throttling
99
+ 3. **Large initial bundle** — use `lazy` for routes and heavy components
100
+ 4. **Deep store trees** — use path syntax updates, not object replacement
@@ -0,0 +1,100 @@
1
+ # Props Patterns
2
+
3
+ > Sources: [Props](https://docs.solidjs.com/concepts/components/props), [Props Tutorials](https://www.solidjs.com/tutorial/props_defaults) — SolidJS Docs
4
+
5
+ ## The Cardinal Rule: Never Destructure Props
6
+
7
+ Props use **getters** internally for reactive tracking. Destructuring extracts the value once, severing the reactive connection permanently.
8
+
9
+ ```typescript
10
+ // WRONG — breaks reactivity
11
+ const MyComponent = ({ name, count }) => {
12
+ return <div>{name}: {count}</div>; // Never updates
13
+ };
14
+
15
+ // WRONG — same problem
16
+ const MyComponent = (props) => {
17
+ const { name } = props; // Captured once, never reactive
18
+ return <div>{name}</div>;
19
+ };
20
+
21
+ // CORRECT — preserves reactivity
22
+ const MyComponent = (props) => {
23
+ return <div>{props.name}: {props.count}</div>;
24
+ };
25
+ ```
26
+
27
+ ## `mergeProps` for Defaults
28
+
29
+ Set default prop values without breaking reactivity:
30
+
31
+ ```typescript
32
+ import { mergeProps } from "solid-js";
33
+
34
+ const Button = (props) => {
35
+ const merged = mergeProps({ variant: "primary", size: "md" }, props);
36
+ return <button class={`btn-${merged.variant} btn-${merged.size}`}>
37
+ {merged.children}
38
+ </button>;
39
+ };
40
+ ```
41
+
42
+ `mergeProps` resolves properties in reverse order — later objects override earlier ones while preserving reactive tracking.
43
+
44
+ ## `splitProps` for Prop Forwarding
45
+
46
+ Safely separate local props from props to forward to children:
47
+
48
+ ```typescript
49
+ import { splitProps } from "solid-js";
50
+
51
+ const Input = (props) => {
52
+ const [local, inputProps] = splitProps(props, ["label", "error"]);
53
+
54
+ return (
55
+ <div>
56
+ <label>{local.label}</label>
57
+ <input {...inputProps} />
58
+ <Show when={local.error}>
59
+ <span class="error">{local.error}</span>
60
+ </Show>
61
+ </div>
62
+ );
63
+ };
64
+ ```
65
+
66
+ You can split into multiple groups:
67
+
68
+ ```typescript
69
+ const [style, events, rest] = splitProps(props, ["class", "style"], ["onClick", "onInput"]);
70
+ ```
71
+
72
+ ## `children` Helper
73
+
74
+ Accessing `props.children` multiple times can create duplicate elements. Use the `children` helper to resolve children once:
75
+
76
+ ```typescript
77
+ import { children } from "solid-js";
78
+
79
+ const ColoredList = (props) => {
80
+ const resolved = children(() => props.children);
81
+
82
+ return <div class="colored">{resolved()}</div>;
83
+ };
84
+ ```
85
+
86
+ The helper resolves any dynamic children into a flat array, memoized for safe repeated access.
87
+
88
+ ## Call Signals Before Passing as Props
89
+
90
+ When passing a signal value to a child component, call the getter. Components shouldn't need to know whether a prop came from a signal or a static value.
91
+
92
+ ```typescript
93
+ // CORRECT — child receives a plain value
94
+ <UserCard name={userName()} age={userAge()} />
95
+
96
+ // Also valid — passing a reactive getter for the child to track
97
+ <UserCard name={userName} /> // child would read props.name()
98
+ ```
99
+
100
+ The first pattern is simpler and recommended for most cases. The second is useful when you want the child to participate in fine-grained tracking.
@@ -0,0 +1,104 @@
1
+ # Reactivity Model
2
+
3
+ > Sources: [Fine-Grained Reactivity](https://docs.solidjs.com/advanced-concepts/fine-grained-reactivity), [Intro to Reactivity](https://docs.solidjs.com/concepts/intro-to-reactivity) — SolidJS Docs
4
+
5
+ ## The Core Difference
6
+
7
+ SolidJS uses **fine-grained reactivity**. Updates target specific DOM elements that depend on changed data — not entire component trees. Components run **once**; only the reactive expressions inside them re-execute.
8
+
9
+ This means:
10
+ - No virtual DOM diffing
11
+ - No component re-renders
12
+ - No stale closure bugs
13
+ - Updates are surgical and automatic
14
+
15
+ ## How It Works
16
+
17
+ ### Signals, Effects, and the Subscription Model
18
+
19
+ The reactive system has two key elements: **signals** (data sources) and **observers** (effects, memos, JSX expressions).
20
+
21
+ 1. A global `currentSubscriber` variable tracks the active observer
22
+ 2. When an observer runs, it sets itself as the current subscriber
23
+ 3. Any signal read during that execution registers the subscriber
24
+ 4. When a signal's value changes, all registered subscribers re-execute
25
+ 5. The subscriber reference is cleared after execution
26
+
27
+ ```typescript
28
+ const [count, setCount] = createSignal(0);
29
+
30
+ // This effect becomes a subscriber of `count`
31
+ createEffect(() => {
32
+ console.log(count()); // Reading count() registers the dependency
33
+ });
34
+
35
+ setCount(1); // Notifies the effect, which re-runs
36
+ ```
37
+
38
+ ### Equality Checks
39
+
40
+ Signals only notify subscribers when the value **actually changes**. Redundant updates (setting the same value) are ignored.
41
+
42
+ ```typescript
43
+ setCount(0); // If count is already 0, no effects fire
44
+ ```
45
+
46
+ ## The Synchronous Contract
47
+
48
+ Reactivity in Solid is **synchronous**. The system registers a subscriber, runs the function, and unregisters — all in one synchronous pass.
49
+
50
+ This means **async operations break tracking**:
51
+
52
+ ```typescript
53
+ // BROKEN — fetch happens after tracking ends
54
+ createEffect(async () => {
55
+ const data = await fetch(url()); // url() is tracked
56
+ setResult(data); // but this runs after tracking ends
57
+ });
58
+
59
+ // CORRECT — use createResource for async
60
+ const [data] = createResource(url, fetchData);
61
+ ```
62
+
63
+ ## Dependency Tracking is Automatic
64
+
65
+ You don't declare dependencies. Solid tracks them by observing which signals are read during execution:
66
+
67
+ ```typescript
68
+ createEffect(() => {
69
+ // Solid automatically knows this depends on firstName() and lastName()
70
+ console.log(`${firstName()} ${lastName()}`);
71
+ });
72
+ ```
73
+
74
+ ### Conditional Tracking
75
+
76
+ If a branch isn't taken, signals in that branch aren't tracked:
77
+
78
+ ```typescript
79
+ const display = createMemo(() => {
80
+ if (!showDetails()) return "Hidden";
81
+ // name() and age() are only tracked when showDetails() is true
82
+ return `${name()}, age ${age()}`;
83
+ });
84
+ ```
85
+
86
+ ## Derive, Don't Sync
87
+
88
+ The golden rule of Solid reactivity:
89
+
90
+ **If a value can be computed from other reactive values, derive it — don't synchronize it with an effect.**
91
+
92
+ ```typescript
93
+ // WRONG — synchronizing state
94
+ createEffect(() => {
95
+ setDoubled(count() * 2);
96
+ });
97
+
98
+ // RIGHT — deriving state
99
+ const doubled = createMemo(() => count() * 2);
100
+ // or simply:
101
+ const doubled = () => count() * 2;
102
+ ```
103
+
104
+ Derived values integrate naturally with the reactive graph. Effects that synchronize state create ordering problems and glitchy intermediate states.
@@ -0,0 +1,78 @@
1
+ # Signals & State
2
+
3
+ > Sources: [Signals](https://docs.solidjs.com/concepts/signals), [Derived Signals](https://docs.solidjs.com/concepts/derived-values/derived-signals) — SolidJS Docs
4
+
5
+ ## createSignal
6
+
7
+ The primary state primitive. Returns a getter function and a setter function.
8
+
9
+ ```typescript
10
+ const [count, setCount] = createSignal(0);
11
+
12
+ // Read: always call as a function
13
+ console.log(count()); // 0
14
+
15
+ // Write: direct value or updater function
16
+ setCount(5);
17
+ setCount(prev => prev + 1);
18
+ ```
19
+
20
+ **Key mental model:** The getter is a function, not a value. You must call `count()` to read it. Forgetting the parentheses is the most common Solid mistake.
21
+
22
+ ## Derived Values
23
+
24
+ ### Inline Derivations
25
+
26
+ For simple computations, a plain function is sufficient:
27
+
28
+ ```typescript
29
+ const doubled = () => count() * 2;
30
+ ```
31
+
32
+ This re-evaluates every time it's called in a reactive context. No caching.
33
+
34
+ ### createMemo
35
+
36
+ For expensive computations or values read in multiple places, use `createMemo` to cache the result:
37
+
38
+ ```typescript
39
+ const sorted = createMemo(() => {
40
+ console.log("Sorting..."); // Only runs when items() changes
41
+ return items().slice().sort((a, b) => a.localeCompare(b));
42
+ });
43
+ ```
44
+
45
+ **When to use `createMemo` vs inline derivation:**
46
+
47
+ | Scenario | Use |
48
+ |---|---|
49
+ | Simple, cheap computation | Inline function `() => ...` |
50
+ | Expensive computation (sort, filter, map) | `createMemo` |
51
+ | Value read in multiple reactive contexts | `createMemo` |
52
+ | Value used once in JSX | Inline function |
53
+
54
+ ## Signals for Functions
55
+
56
+ If you need to store a function as a signal value, use the callback form of the setter to avoid Solid treating it as an updater:
57
+
58
+ ```typescript
59
+ const [handler, setHandler] = createSignal<() => void>(() => {});
60
+
61
+ // WRONG — Solid calls this as an updater
62
+ setHandler(myFunction);
63
+
64
+ // RIGHT — wrap in callback
65
+ setHandler(() => myFunction);
66
+ ```
67
+
68
+ ## When to Use Signals vs Stores
69
+
70
+ | Data shape | Use |
71
+ |---|---|
72
+ | Single primitive value | `createSignal` |
73
+ | Simple object (few properties, flat) | `createSignal` |
74
+ | Complex nested object | `createStore` |
75
+ | Array of objects with updates | `createStore` |
76
+ | Shared state across components | `createStore` + context |
77
+
78
+ See [Stores & Nested State](stores-and-nested-state.md) for store patterns.