or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

component-system.mdcontext-scoping.mdcontrol-flow.mdindex.mdreactive-primitives.mdresources-async.mdstore-management.mdweb-rendering.md

reactive-primitives.mddocs/

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

```