0
# Reactive Primitives
1
2
Core reactive system providing signals, effects, and memos for building reactive applications with automatic dependency tracking and fine-grained updates.
3
4
## Capabilities
5
6
### Signals
7
8
Create reactive state with getter and setter functions that automatically track dependencies and trigger updates.
9
10
```typescript { .api }
11
/**
12
* Creates a reactive state with getter and setter functions
13
* @param value - Initial value for the signal
14
* @param options - Configuration options for the signal
15
* @returns Tuple of [getter, setter] functions
16
*/
17
function createSignal<T>(): Signal<T | undefined>;
18
function createSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;
19
20
interface SignalOptions<T> {
21
equals?: false | ((prev: T, next: T) => boolean);
22
name?: string;
23
internal?: boolean;
24
}
25
```
26
27
**Usage Examples:**
28
29
```typescript
30
import { createSignal } from "solid-js";
31
32
// Basic signal
33
const [count, setCount] = createSignal(0);
34
console.log(count()); // 0
35
setCount(5);
36
console.log(count()); // 5
37
38
// Signal with custom equality
39
const [user, setUser] = createSignal(
40
{ name: "John", age: 30 },
41
{ equals: (prev, next) => prev.name === next.name && prev.age === next.age }
42
);
43
44
// Functional updates
45
setCount(prev => prev + 1);
46
setUser(prev => ({ ...prev, age: prev.age + 1 }));
47
```
48
49
### Effects
50
51
Create reactive computations that run automatically when their dependencies change.
52
53
```typescript { .api }
54
/**
55
* Creates a reactive computation that runs after render phase
56
* @param fn - Effect function that tracks dependencies
57
* @param value - Initial value for the effect
58
* @param options - Configuration options
59
*/
60
function createEffect<T>(fn: EffectFunction<T>, value?: T, options?: EffectOptions): void;
61
62
/**
63
* Creates a reactive computation during render phase
64
* @param fn - Effect function that tracks dependencies
65
* @param value - Initial value for the effect
66
* @param options - Configuration options
67
*/
68
function createRenderEffect<T>(fn: EffectFunction<T>, value?: T, options?: EffectOptions): void;
69
70
/**
71
* Creates a reactive computation that runs immediately before render
72
* @param fn - Effect function that tracks dependencies
73
* @param value - Initial value for the effect
74
* @param options - Configuration options
75
*/
76
function createComputed<T>(fn: EffectFunction<T>, value?: T, options?: EffectOptions): void;
77
78
type EffectFunction<Prev, Next extends Prev = Prev> = (v: Prev) => Next;
79
80
interface EffectOptions {
81
name?: string;
82
}
83
```
84
85
**Usage Examples:**
86
87
```typescript
88
import { createSignal, createEffect } from "solid-js";
89
90
const [count, setCount] = createSignal(0);
91
92
// Basic effect
93
createEffect(() => {
94
console.log("Count is:", count());
95
});
96
97
// Effect with previous value
98
createEffect((prev) => {
99
const current = count();
100
console.log(`Count changed from ${prev} to ${current}`);
101
return current;
102
}, count());
103
104
// Effect with cleanup
105
createEffect(() => {
106
const timer = setInterval(() => {
107
setCount(c => c + 1);
108
}, 1000);
109
110
onCleanup(() => clearInterval(timer));
111
});
112
```
113
114
### Memos
115
116
Create derived reactive values that are memoized and only recalculate when their dependencies change.
117
118
```typescript { .api }
119
/**
120
* Creates a readonly derived reactive memoized signal
121
* @param fn - Memo function that computes the derived value
122
* @param value - Initial value for the memo
123
* @param options - Configuration options
124
* @returns Accessor function for the memoized value
125
*/
126
function createMemo<T>(fn: EffectFunction<T>, value?: T, options?: MemoOptions<T>): Accessor<T>;
127
128
interface MemoOptions<T> extends EffectOptions {
129
equals?: false | ((prev: T, next: T) => boolean);
130
}
131
```
132
133
**Usage Examples:**
134
135
```typescript
136
import { createSignal, createMemo } from "solid-js";
137
138
const [firstName, setFirstName] = createSignal("John");
139
const [lastName, setLastName] = createSignal("Doe");
140
141
// Basic memo
142
const fullName = createMemo(() => `${firstName()} ${lastName()}`);
143
144
// Memo with custom equality
145
const expensiveComputation = createMemo(
146
() => someExpensiveFunction(firstName(), lastName()),
147
undefined,
148
{ equals: (prev, next) => prev.id === next.id }
149
);
150
151
// Using memos in components
152
function UserProfile() {
153
const displayName = createMemo(() => {
154
const first = firstName();
155
const last = lastName();
156
return first && last ? `${first} ${last}` : first || last || "Anonymous";
157
});
158
159
return <div>Hello, {displayName()}!</div>;
160
}
161
```
162
163
### Advanced Reactive Utilities
164
165
Create complex reactive patterns and manage reactive ownership.
166
167
```typescript { .api }
168
/**
169
* Creates a reactive computation with flexible tracking
170
* @param onInvalidate - Function called when dependencies change
171
* @param options - Configuration options
172
* @returns Function to run reactive computations
173
*/
174
function createReaction(
175
onInvalidate: () => void,
176
options?: EffectOptions
177
): (fn: () => void) => void;
178
179
/**
180
* Creates a reactive computation that only runs when browser is idle
181
* @param source - Source accessor to watch
182
* @param options - Configuration options
183
* @returns Deferred accessor
184
*/
185
function createDeferred<T>(
186
source: Accessor<T>,
187
options?: DeferredOptions<T>
188
): Accessor<T>;
189
190
/**
191
* Creates a conditional signal for O(2) instead of O(n) operations
192
* @param source - Source accessor to watch
193
* @param fn - Equality function for comparison
194
* @param options - Configuration options
195
* @returns Function that checks if key matches current value
196
*/
197
function createSelector<T, U>(
198
source: Accessor<T>,
199
fn?: (a: U, b: T) => boolean,
200
options?: BaseOptions
201
): (key: U) => boolean;
202
203
interface DeferredOptions<T> {
204
timeoutMs?: number;
205
equals?: false | ((prev: T, next: T) => boolean);
206
}
207
```
208
209
### Control Flow Utilities
210
211
Control reactive execution and dependency tracking.
212
213
```typescript { .api }
214
/**
215
* Batches reactive updates within the block
216
* @param fn - Function to run in batch
217
* @returns Return value of the function
218
*/
219
function batch<T>(fn: () => T): T;
220
221
/**
222
* Ignores tracking context inside its scope
223
* @param fn - Function to run without tracking
224
* @returns Return value of the function
225
*/
226
function untrack<T>(fn: () => T): T;
227
228
/**
229
* Makes dependencies of a computation explicit
230
* @param deps - Dependencies to track
231
* @param fn - Function to run when dependencies change
232
* @param options - Configuration options
233
* @returns Return value of the function
234
*/
235
function on<S, T>(
236
deps: Accessor<S> | Accessor<S>[],
237
fn: (input: S, prevInput?: S, prevValue?: T) => T,
238
options?: OnOptions
239
): EffectFunction<undefined, T>;
240
241
interface OnOptions {
242
defer?: boolean;
243
}
244
```
245
246
### Lifecycle Hooks
247
248
Manage component lifecycle and cleanup in reactive computations.
249
250
```typescript { .api }
251
/**
252
* Runs an effect only after initial render on mount
253
* @param fn - Function to run on mount
254
*/
255
function onMount(fn: () => void): void;
256
257
/**
258
* Runs an effect once before the reactive scope is disposed
259
* @param fn - Function to run on cleanup
260
* @returns The cleanup function
261
*/
262
function onCleanup<T extends () => any>(fn: T): T;
263
264
/**
265
* Runs an effect whenever an error is thrown within child scopes
266
* @param fn - Function to run with the caught error
267
* @param handler - Error handler function
268
* @returns Return value or undefined if error occurred
269
*/
270
function catchError<T>(
271
fn: () => T,
272
handler: (err: Error) => void
273
): T | undefined;
274
```
275
276
**Usage Examples:**
277
278
```typescript
279
import { createSignal, createEffect, onMount, onCleanup, batch } from "solid-js";
280
281
function Timer() {
282
const [time, setTime] = createSignal(0);
283
const [isRunning, setIsRunning] = createSignal(false);
284
285
onMount(() => {
286
console.log("Timer component mounted");
287
});
288
289
createEffect(() => {
290
if (isRunning()) {
291
const interval = setInterval(() => {
292
setTime(t => t + 1);
293
}, 1000);
294
295
onCleanup(() => {
296
clearInterval(interval);
297
console.log("Timer cleanup");
298
});
299
}
300
});
301
302
const reset = () => {
303
batch(() => {
304
setTime(0);
305
setIsRunning(false);
306
});
307
};
308
309
return (
310
<div>
311
<div>Time: {time()}s</div>
312
<button onClick={() => setIsRunning(!isRunning())}>
313
{isRunning() ? "Stop" : "Start"}
314
</button>
315
<button onClick={reset}>Reset</button>
316
</div>
317
);
318
}
319
```
320
321
### Advanced Integration
322
323
Enable integration with external reactive systems and custom scheduling.
324
325
```typescript { .api }
326
/**
327
* Enables integration with external reactive systems
328
* @param factory - Factory function for external sources
329
* @param untrack - Function to untrack dependencies (defaults to provided untrack)
330
*/
331
function enableExternalSource(
332
factory: ExternalSourceFactory,
333
untrack?: <V>(fn: () => V) => V
334
): void;
335
336
/**
337
* Enables custom scheduling for updates
338
* @param scheduler - Custom scheduler function (defaults to requestCallback)
339
*/
340
function enableScheduling(scheduler?: (fn: () => void) => void): void;
341
342
type ExternalSourceFactory = <T>(
343
fn: (track: Accessor<T>) => void,
344
trigger: () => void
345
) => () => void;
346
```
347
348
### Reactive Ownership and Scoping
349
350
Create and manage reactive ownership contexts for proper cleanup and component isolation.
351
352
```typescript { .api }
353
/**
354
* Creates a reactive root context for ownership management
355
* @param fn - Function to run in the root context, receives dispose function
356
* @param detachedOwner - Optional owner to detach from
357
* @returns Return value of the function
358
*/
359
function createRoot<T>(fn: (dispose: () => void) => T, detachedOwner?: Owner): T;
360
361
/**
362
* Gets the current reactive owner context
363
* @returns Current owner or null if none
364
*/
365
function getOwner(): Owner | null;
366
367
/**
368
* Runs a function within a specific owner context
369
* @param owner - Owner context to run within
370
* @param fn - Function to run
371
* @returns Return value of the function
372
*/
373
function runWithOwner<T>(owner: Owner | null, fn: () => T): T | undefined;
374
375
/**
376
* Resolves children in a reactive context
377
* @param fn - Function that returns children
378
* @returns Resolved children accessor
379
*/
380
function children(fn: () => any): ChildrenReturn;
381
382
type RootFunction<T> = (dispose: () => void) => T;
383
type ChildrenReturn = Accessor<ResolvedChildren> & { toArray: () => ResolvedJSXElement[] };
384
```
385
386
**Usage Examples:**
387
388
```typescript
389
import { createRoot, createSignal, createEffect, getOwner, runWithOwner } from "solid-js";
390
391
// Basic root usage
392
const dispose = createRoot((dispose) => {
393
const [count, setCount] = createSignal(0);
394
395
createEffect(() => {
396
console.log("Count:", count());
397
});
398
399
// Clean up when needed
400
setTimeout(() => dispose(), 5000);
401
402
return dispose;
403
});
404
405
// Owner management
406
function ParentComponent() {
407
const owner = getOwner();
408
409
const childComputation = () => {
410
// This will run in the parent's context
411
runWithOwner(owner, () => {
412
createEffect(() => {
413
console.log("Child effect in parent context");
414
});
415
});
416
};
417
418
return <div onClick={childComputation}>Click me</div>;
419
}
420
```
421
422
### Context System
423
424
Create and consume context for passing data through the component tree.
425
426
```typescript { .api }
427
/**
428
* Creates a context for passing data through component tree
429
* @param defaultValue - Default value when no provider is found
430
* @param options - Optional configuration
431
* @returns Context object with id and Provider
432
*/
433
function createContext<T>(defaultValue?: T, options?: EffectOptions): Context<T>;
434
435
/**
436
* Consumes a context value from the nearest provider
437
* @param context - Context to consume
438
* @returns Current context value
439
*/
440
function useContext<T>(context: Context<T>): T;
441
442
interface Context<T> {
443
id: symbol;
444
Provider: ContextProviderComponent<T>;
445
defaultValue: T;
446
}
447
448
type ContextProviderComponent<T> = Component<{
449
value: T;
450
children: JSX.Element;
451
}>;
452
```
453
454
**Usage Examples:**
455
456
```typescript
457
import { createContext, useContext } from "solid-js";
458
459
// Create theme context
460
const ThemeContext = createContext("light");
461
462
function App() {
463
return (
464
<ThemeContext.Provider value="dark">
465
<UserInterface />
466
</ThemeContext.Provider>
467
);
468
}
469
470
function UserInterface() {
471
const theme = useContext(ThemeContext);
472
473
return (
474
<div class={theme === "dark" ? "dark-theme" : "light-theme"}>
475
Current theme: {theme}
476
</div>
477
);
478
}
479
```
480
481
### Async Resources
482
483
Handle asynchronous data loading with reactive resources.
484
485
```typescript { .api }
486
/**
487
* Creates a resource for handling async data
488
* @param fetcher - Function to fetch the resource data
489
* @param options - Configuration options
490
* @returns Resource tuple [resource accessor, resource actions]
491
*/
492
function createResource<T, R = unknown>(
493
fetcher: ResourceFetcher<true, T, R>,
494
options?: ResourceOptions<T, true>
495
): ResourceReturn<T, R>;
496
497
/**
498
* Creates a resource with a source signal
499
* @param source - Source signal that triggers refetch
500
* @param fetcher - Function to fetch the resource data
501
* @param options - Configuration options
502
* @returns Resource tuple [resource accessor, resource actions]
503
*/
504
function createResource<T, S, R = unknown>(
505
source: ResourceSource<S>,
506
fetcher: ResourceFetcher<S, T, R>,
507
options?: ResourceOptions<T, S>
508
): ResourceReturn<T, R>;
509
510
type ResourceSource<S> = S | false | null | undefined | (() => S | false | null | undefined);
511
type ResourceFetcher<S, T, R = unknown> = (source: S, info: ResourceFetcherInfo<T, R>) => T | Promise<T>;
512
513
interface ResourceFetcherInfo<T, R = unknown> {
514
value: T | undefined;
515
refetching: R | boolean;
516
}
517
518
interface ResourceOptions<T, S = unknown> {
519
initialValue?: T;
520
name?: string;
521
deferStream?: boolean;
522
ssrLoadFrom?: "initial" | "server";
523
storage?: (init: T | undefined) => [Accessor<T | undefined>, Setter<T | undefined>];
524
onHydrated?: (k: S | undefined, info: { value: T | undefined }) => void;
525
}
526
527
type ResourceReturn<T, R = unknown> = [Resource<T>, ResourceActions<T | undefined, R>];
528
529
type ResourceActions<T, R = unknown> = {
530
mutate: Setter<T>;
531
refetch: (info?: R) => T | Promise<T> | undefined | null;
532
};
533
```
534
535
### Transitions
536
537
Manage async state transitions and provide loading states.
538
539
```typescript { .api }
540
/**
541
* Starts a transition and returns a promise
542
* @param fn - Function to run in transition
543
* @returns Promise that resolves when transition completes
544
*/
545
function startTransition(fn: () => unknown): Promise<void>;
546
547
/**
548
* Hook for managing transition state
549
* @returns Tuple of [isPending accessor, startTransition function]
550
*/
551
function useTransition(): [Accessor<boolean>, (fn: () => void) => Promise<void>];
552
553
type Transition = [Accessor<boolean>, (fn: () => void) => Promise<void>];
554
```
555
556
**Usage Examples:**
557
558
```typescript
559
import { createResource, startTransition, useTransition } from "solid-js";
560
561
// Basic resource
562
const [user] = createResource(fetchUser);
563
564
// Resource with source
565
const [userId, setUserId] = createSignal(1);
566
const [userData] = createResource(userId, fetchUserById);
567
568
// Transitions
569
function App() {
570
const [isPending, start] = useTransition();
571
572
const handleUpdate = () => {
573
start(() => {
574
// This will show pending state
575
updateSomething();
576
});
577
};
578
579
return (
580
<div>
581
<button onClick={handleUpdate} disabled={isPending()}>
582
{isPending() ? "Updating..." : "Update"}
583
</button>
584
</div>
585
);
586
}
587
```
588
589
## Types
590
591
### Core Signal Types
592
593
```typescript { .api }
594
type Accessor<T> = () => T;
595
type Setter<T> = {
596
(value: T): T;
597
(fn: (prev: T) => T): T;
598
};
599
type Signal<T> = [get: Accessor<T>, set: Setter<T>];
600
```
601
602
### Reactive Ownership Types
603
604
```typescript { .api }
605
interface Owner {
606
owned: Computation<any>[] | null;
607
cleanups: (() => void)[] | null;
608
owner: Owner | null;
609
context: any;
610
sourceMap?: Set<Computation<any>>;
611
name?: string;
612
}
613
614
interface Computation<T> {
615
fn: EffectFunction<any, T>;
616
state: number;
617
sources: Computation<any>[] | null;
618
sourceSlots: number[] | null;
619
value?: T;
620
age: number;
621
updatedAt?: number;
622
}
623
```
624
625
### Resource Types
626
627
```typescript { .api }
628
type Resource<T> = Accessor<T | undefined> & {
629
loading: boolean;
630
error: any;
631
latest: T | undefined;
632
state: "unresolved" | "pending" | "ready" | "refreshing" | "errored";
633
};
634
635
type ResolvedJSXElement = Exclude<JSX.Element, JSX.ArrayElement>;
636
type ResolvedChildren = ResolvedJSXElement | ResolvedJSXElement[];
637
```