claude-toolkit 0.16.0 → 0.17.0

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 (68) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +54 -13
  3. package/bin/cli.ts +75 -19
  4. package/package.json +6 -7
  5. package/src/generator.ts +64 -26
  6. package/src/refresh-hint.ts +95 -0
  7. package/bin/postinstall.mjs +0 -69
  8. package/docs/README.md +0 -59
  9. package/docs/agents/code-reviewer.md +0 -76
  10. package/docs/agents/github-workflow.md +0 -98
  11. package/docs/best-practices/capacitor/README.md +0 -64
  12. package/docs/best-practices/capacitor/capacitor-8.md +0 -60
  13. package/docs/best-practices/capacitor/capgo-setup.md +0 -151
  14. package/docs/best-practices/capacitor/channels-and-rollouts.md +0 -101
  15. package/docs/best-practices/capacitor/live-updates-ota.md +0 -75
  16. package/docs/best-practices/capacitor/security-encryption.md +0 -68
  17. package/docs/best-practices/solidjs/README.md +0 -43
  18. package/docs/best-practices/solidjs/anti-patterns.md +0 -166
  19. package/docs/best-practices/solidjs/component-patterns.md +0 -131
  20. package/docs/best-practices/solidjs/context-and-global-state.md +0 -131
  21. package/docs/best-practices/solidjs/control-flow.md +0 -124
  22. package/docs/best-practices/solidjs/data-fetching.md +0 -205
  23. package/docs/best-practices/solidjs/effects-and-lifecycle.md +0 -113
  24. package/docs/best-practices/solidjs/performance.md +0 -100
  25. package/docs/best-practices/solidjs/props-patterns.md +0 -100
  26. package/docs/best-practices/solidjs/reactivity-model.md +0 -104
  27. package/docs/best-practices/solidjs/signals-and-state.md +0 -78
  28. package/docs/best-practices/solidjs/stores-and-nested-state.md +0 -111
  29. package/docs/best-practices/solidjs/typescript-integration.md +0 -186
  30. package/docs/best-practices/testing/README.md +0 -84
  31. package/docs/best-practices/testing/playwright-e2e.md +0 -649
  32. package/docs/best-practices/testing/storybook-interaction.md +0 -455
  33. package/docs/best-practices/testing/vitest-unit.md +0 -451
  34. package/docs/best-practices/typescript/README.md +0 -45
  35. package/docs/best-practices/typescript/any-and-unknown.md +0 -73
  36. package/docs/best-practices/typescript/deriving-vs-decoupling.md +0 -83
  37. package/docs/best-practices/typescript/discriminated-unions.md +0 -75
  38. package/docs/best-practices/typescript/enums-alternatives.md +0 -72
  39. package/docs/best-practices/typescript/essential-patterns.md +0 -119
  40. package/docs/best-practices/typescript/generics-patterns.md +0 -105
  41. package/docs/best-practices/typescript/micro-opinions.md +0 -87
  42. package/docs/best-practices/typescript/runtime-validation.md +0 -62
  43. package/docs/best-practices/typescript/satisfies-operator.md +0 -100
  44. package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +0 -160
  45. package/docs/best-practices/typescript/type-organization.md +0 -64
  46. package/docs/best-practices/typescript/type-vs-interface.md +0 -80
  47. package/docs/commands/code-quality.md +0 -36
  48. package/docs/commands/onboard.md +0 -63
  49. package/docs/commands/pr-review.md +0 -91
  50. package/docs/commands/pr-summary.md +0 -50
  51. package/docs/commands/proto-check.md +0 -59
  52. package/docs/commands/ticket.md +0 -56
  53. package/docs/skills/code-style.md +0 -26
  54. package/docs/skills/systematic-debugging.md +0 -70
  55. package/docs/skills/testing-patterns.md +0 -140
  56. package/docs/skills/typescript-conventions.md +0 -283
  57. package/docs/skills/verification-before-completion.md +0 -103
  58. package/docs/stacks/capacitor-ota.md +0 -145
  59. package/docs/stacks/cloudflare-d1-kv.md +0 -144
  60. package/docs/stacks/esnext-idioms.md +0 -25
  61. package/docs/stacks/i18n-typesafe.md +0 -141
  62. package/docs/stacks/playwright-patterns.md +0 -179
  63. package/docs/stacks/protobuf-contracts.md +0 -85
  64. package/docs/stacks/rust-wasm-patterns.md +0 -106
  65. package/docs/stacks/solidjs-patterns.md +0 -110
  66. package/docs/stacks/storybook-patterns.md +0 -177
  67. package/docs/stacks/vanilla-extract-patterns.md +0 -115
  68. package/docs/stacks/vite-vitest-patterns.md +0 -485
@@ -1,131 +0,0 @@
1
- # Context & Global State
2
-
3
- > Sources: [Context](https://docs.solidjs.com/concepts/context), [Complex State Management](https://docs.solidjs.com/guides/complex-state-management) — SolidJS Docs
4
-
5
- ## When to Use Context
6
-
7
- Context solves **prop drilling** — passing state through many component layers that don't use it themselves. Use it for:
8
-
9
- - Theme / appearance settings
10
- - Authentication state
11
- - Feature flags
12
- - Any state needed by many descendants
13
-
14
- **When NOT to use context:**
15
- - If only a few components need the data, pass props directly
16
- - If you can restructure the component tree to avoid drilling
17
- - For truly global singletons, a module-level signal may be simpler
18
-
19
- ## Basic Context Pattern
20
-
21
- ```typescript
22
- import { createContext, useContext } from "solid-js";
23
- import { createStore } from "solid-js/store";
24
-
25
- // 1. Create context
26
- const CounterContext = createContext<{
27
- count: number;
28
- increment: () => void;
29
- }>();
30
-
31
- // 2. Create provider component
32
- function CounterProvider(props: ParentProps) {
33
- const [state, setState] = createStore({ count: 0 });
34
- const value = {
35
- get count() { return state.count; },
36
- increment() { setState("count", c => c + 1); },
37
- };
38
-
39
- return (
40
- <CounterContext.Provider value={value}>
41
- {props.children}
42
- </CounterContext.Provider>
43
- );
44
- }
45
-
46
- // 3. Consume with useContext
47
- function Counter() {
48
- const ctx = useContext(CounterContext);
49
- return <button onClick={ctx.increment}>{ctx.count}</button>;
50
- }
51
- ```
52
-
53
- ## Safe useContext with Error Throwing
54
-
55
- `useContext` returns `undefined` when no provider exists above. Create a custom hook that throws a helpful error:
56
-
57
- ```typescript
58
- function useCounter() {
59
- const ctx = useContext(CounterContext);
60
- if (!ctx) {
61
- throw new Error("useCounter must be used within a CounterProvider");
62
- }
63
- return ctx;
64
- }
65
- ```
66
-
67
- This also **narrows the TypeScript type** — no more `| undefined`.
68
-
69
- ## Context + Store Pattern (Recommended)
70
-
71
- Combine `createStore` with context for scalable state. Stores give you fine-grained reactivity; context distributes it without prop drilling.
72
-
73
- ```typescript
74
- function createTaskStore() {
75
- const [state, setState] = createStore({
76
- tasks: [] as Task[],
77
- filter: "all" as "all" | "active" | "completed",
78
- });
79
-
80
- return {
81
- get tasks() { return state.tasks; },
82
- get filter() { return state.filter; },
83
- addTask(text: string) {
84
- setState("tasks", state.tasks.length, {
85
- id: crypto.randomUUID(),
86
- text,
87
- completed: false,
88
- });
89
- },
90
- toggleTask(id: string) {
91
- setState("tasks", t => t.id === id, "completed", c => !c);
92
- },
93
- setFilter(filter: "all" | "active" | "completed") {
94
- setState("filter", filter);
95
- },
96
- };
97
- }
98
-
99
- const TaskContext = createContext<ReturnType<typeof createTaskStore>>();
100
-
101
- function TaskProvider(props: ParentProps) {
102
- return (
103
- <TaskContext.Provider value={createTaskStore()}>
104
- {props.children}
105
- </TaskContext.Provider>
106
- );
107
- }
108
- ```
109
-
110
- **Why this works well in Solid:** Updating a signal at the top of the tree does NOT cause children to re-render. Only the specific reactive expressions that read the changed property update. There is no performance penalty for high-level state.
111
-
112
- ## HMR Consideration
113
-
114
- To avoid recreating context during Hot Module Replacement, define `createContext` in its **own module** (separate file), not inline in a component file.
115
-
116
- ## Module-Level Signals vs Context
117
-
118
- For truly global, app-wide singletons (e.g., a theme toggle), a module-level signal is simpler:
119
-
120
- ```typescript
121
- // theme.ts
122
- export const [theme, setTheme] = createSignal<"light" | "dark">("light");
123
-
124
- // Any component can import directly
125
- import { theme, setTheme } from "./theme";
126
- ```
127
-
128
- Use context instead when:
129
- - Different subtrees need different values (e.g., per-user state)
130
- - You need testability (mock providers in tests)
131
- - The state has complex dependencies
@@ -1,124 +0,0 @@
1
- # Control Flow
2
-
3
- > Sources: [Show](https://docs.solidjs.com/reference/components/show), [For](https://docs.solidjs.com/reference/components/for), [SolidJS Tutorials](https://www.solidjs.com/tutorial) — SolidJS Docs
4
-
5
- ## Why Control Flow Components?
6
-
7
- In Solid, **avoid JavaScript-native conditionals and `.map()` in JSX**. Solid's control flow components provide explicit boundaries that the compiler can optimize — they track dependencies and minimize DOM operations.
8
-
9
- ## `<Show>` — Conditional Rendering
10
-
11
- Renders children when `when` is truthy. Optionally provides a `fallback`.
12
-
13
- ```typescript
14
- <Show when={user()} fallback={<LoginPrompt />}>
15
- <Dashboard />
16
- </Show>
17
- ```
18
-
19
- ### Callback Form for Type Narrowing
20
-
21
- The callback form provides a non-null accessor — essential for TypeScript:
22
-
23
- ```typescript
24
- <Show when={user()}>
25
- {(nonNullUser) => <span>{nonNullUser().name}</span>}
26
- </Show>
27
- ```
28
-
29
- TypeScript cannot narrow accessor types through control flow, so the callback form is the idiomatic way to access narrowed values.
30
-
31
- ## `<For>` — List Rendering
32
-
33
- Renders a list, keyed by **reference** (not index). Only re-renders items that actually change.
34
-
35
- ```typescript
36
- <For each={items()}>
37
- {(item, index) => (
38
- <li>{index()}: {item.name}</li>
39
- )}
40
- </For>
41
- ```
42
-
43
- - `item` is the value (not reactive for primitives)
44
- - `index()` is a signal — call it as a function
45
- - Items are tracked by reference — avoid recreating objects
46
-
47
- ### `<Index>` — Keyed by Index
48
-
49
- For primitive arrays where values change but positions are stable:
50
-
51
- ```typescript
52
- <Index each={names()}>
53
- {(name, i) => <li>{i}: {name()}</li>}
54
- </Index>
55
- ```
56
-
57
- - `name` is a signal (reactive)
58
- - `i` is a plain number (not reactive)
59
-
60
- **Rule of thumb:** Use `<For>` for arrays of objects, `<Index>` for arrays of primitives.
61
-
62
- ## `<Switch>` / `<Match>` — Multi-Branch Conditional
63
-
64
- ```typescript
65
- <Switch fallback={<p>Unknown state</p>}>
66
- <Match when={state() === "loading"}>
67
- <Spinner />
68
- </Match>
69
- <Match when={state() === "error"}>
70
- <ErrorDisplay />
71
- </Match>
72
- <Match when={state() === "ready"}>
73
- <Content />
74
- </Match>
75
- </Switch>
76
- ```
77
-
78
- First matching `<Match>` wins. Use `fallback` on `<Switch>` for the default case.
79
-
80
- ## `<ErrorBoundary>` — Error Catching
81
-
82
- Catches errors thrown in child components and renders a fallback:
83
-
84
- ```typescript
85
- <ErrorBoundary fallback={(err, reset) => (
86
- <div>
87
- <p>Error: {err.message}</p>
88
- <button onClick={reset}>Retry</button>
89
- </div>
90
- )}>
91
- <RiskyComponent />
92
- </ErrorBoundary>
93
- ```
94
-
95
- The `reset` function re-renders the children, giving the component a fresh chance.
96
-
97
- ## `<Suspense>` — Async Loading States
98
-
99
- Displays a fallback while waiting for async resources to resolve:
100
-
101
- ```typescript
102
- <Suspense fallback={<Skeleton />}>
103
- <UserProfile /> {/* Uses createResource internally */}
104
- </Suspense>
105
- ```
106
-
107
- Suspense detects any `createResource` read within its boundary and holds rendering until all resolve. Nested Suspense boundaries allow granular loading states.
108
-
109
- ## `<Dynamic>` — Dynamic Component
110
-
111
- Render a component determined at runtime:
112
-
113
- ```typescript
114
- <Dynamic component={isAdmin() ? AdminPanel : UserPanel} user={user()} />
115
- ```
116
-
117
- ## Don't Use
118
-
119
- | Instead of | Use |
120
- |---|---|
121
- | `{condition && <Component />}` | `<Show when={condition}>` |
122
- | `{condition ? <A /> : <B />}` | `<Show when={condition} fallback={<B />}>` |
123
- | `{items.map(i => ...)}` | `<For each={items()}>` |
124
- | Nested ternaries | `<Switch>` / `<Match>` |
@@ -1,205 +0,0 @@
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.
@@ -1,113 +0,0 @@
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
@@ -1,100 +0,0 @@
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