From 30ba3ded453cef91f3cfa4c4c760ec58fa31f683 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 12:11:24 -0700 Subject: [PATCH 01/88] docs: enhance documentation for batch, onCleanup, onMount, and untrack --- src/routes/reference/lifecycle/on-cleanup.mdx | 72 ++++++++++++++--- src/routes/reference/lifecycle/on-mount.mdx | 58 ++++++++++++-- .../reference/reactive-utilities/batch.mdx | 78 ++++++++++++------- .../reference/reactive-utilities/untrack.mdx | 74 ++++++++++++++++-- 4 files changed, 232 insertions(+), 50 deletions(-) diff --git a/src/routes/reference/lifecycle/on-cleanup.mdx b/src/routes/reference/lifecycle/on-cleanup.mdx index f2963120e..e8ae07337 100644 --- a/src/routes/reference/lifecycle/on-cleanup.mdx +++ b/src/routes/reference/lifecycle/on-cleanup.mdx @@ -16,34 +16,56 @@ description: >- components unmount or tracking scopes dispose. Essential for proper cleanup. --- -`onCleanup` registers a cleanup method that executes on disposal and recalculation of the current tracking scope. -Can be used anywhere to clean up any side effects left behind by initialization. +`onCleanup` registers a cleanup function on the current reactive scope. +The cleanup runs when that scope is disposed or refreshed. -When used in a Component, it runs when the component is unmounted. -When used in tracking scope, such [`createEffect`](/reference/basic-reactivity/create-effect), [`createMemo`](/reference/basic-reactivity/create-memo) or a [`createRoot`](/reference/reactive-utilities/create-root), it runs when the tracking scope is disposed or refreshed. +## Import ```ts import { onCleanup } from "solid-js"; +``` + +## Type -function onCleanup(fn: () => void): void; +```ts +function onCleanup any>(fn: T): T; ``` -Without the `onCleanup` function, the event listener would remain attached to the `document` even after the component is removed from the page. -This can cause memory leaks and other issues. +## Parameters + +### `fn` + +- **Type:** `() => any` +- **Required:** Yes + +Cleanup function registered on the current reactive scope. + +## Return value + +- **Type:** `T` + +Returns the same function that was passed in. + +## Behavior + +- In a component, the cleanup runs when that component is unmounted. +- In a tracking scope such as [`createEffect`](/reference/basic-reactivity/create-effect), [`createMemo`](/reference/basic-reactivity/create-memo), or [`createRoot`](/reference/reactive-utilities/create-root), the cleanup runs when that scope is disposed or re-executes. +- Multiple cleanup functions run when the owning scope is cleaned up. +- On the server, cleanup also runs when server-side reactive branches are disposed. + +## Examples + +### Remove an event listener ```tsx import { createSignal, onCleanup } from "solid-js"; const Component = () => { const [count, setCount] = createSignal(0); - const handleClick = () => setCount((value) => value + 1); document.addEventListener("click", handleClick); - /** - * Remove the event listener when the component is removed/unmounted from the page. - */ onCleanup(() => { document.removeEventListener("click", handleClick); }); @@ -51,3 +73,31 @@ const Component = () => { return
Document has been clicked {count()} times
; }; ``` + +### Clean up before an effect re-runs + +```tsx +import { createEffect, createSignal, onCleanup } from "solid-js"; + +function Example() { + const [topic, setTopic] = createSignal("news"); + + createEffect(() => { + const currentTopic = topic(); + + console.log("subscribing to", currentTopic); + + onCleanup(() => { + console.log("cleaning up", currentTopic); + }); + }); + + return ; +} +``` + +## Related + +- [`onMount`](/reference/lifecycle/on-mount) +- [`createEffect`](/reference/basic-reactivity/create-effect) +- [`createRoot`](/reference/reactive-utilities/create-root) diff --git a/src/routes/reference/lifecycle/on-mount.mdx b/src/routes/reference/lifecycle/on-mount.mdx index eac027ec4..ce3f27ef9 100644 --- a/src/routes/reference/lifecycle/on-mount.mdx +++ b/src/routes/reference/lifecycle/on-mount.mdx @@ -16,28 +16,74 @@ description: >- refs, setting up third-party libraries, and one-time initializations. --- -Registers a method that runs after initial rendering is done and the elements are mounted to the page. -Ideal for using [refs](/reference/jsx-attributes/ref) and managing other one-time setup. +`onMount` registers a function that runs once after the initial render for the current component or root. -```tsx +## Import + +```ts import { onMount } from "solid-js"; +``` + +## Type +```ts function onMount(fn: () => void): void; ``` -This is an alias for an effect that is non-tracking, meaning that it is equivalent to a [`createEffect`](/reference/basic-reactivity/create-effect) with no dependencies. +## Parameters + +### `fn` + +- **Type:** `() => void` +- **Required:** Yes + +Function executed once on mount. + +## Return value + +`onMount` does not return a value. + +## Behavior + +- Runs once after the initial render. +- Does not track reactive dependencies inside `fn`. +- Internally, `onMount(fn)` is equivalent to `createEffect(() => untrack(fn))`. +- Runs after refs have been assigned. + +## Examples + +### Access a ref after mount ```tsx -// example that shows how to use onMount to get a reference to an element import { onMount } from "solid-js"; function MyComponent() { let ref: HTMLButtonElement; - // when the component is mounted, the button will be disabled onMount(() => { ref.disabled = true; }); + return ; } ``` + +### Run one-time browser setup + +```tsx +import { onMount } from "solid-js"; + +function Example() { + onMount(() => { + console.log(window.location.pathname); + }); + + return
Mounted
; +} +``` + +## Related + +- [`onCleanup`](/reference/lifecycle/on-cleanup) +- [`createEffect`](/reference/basic-reactivity/create-effect) +- [`untrack`](/reference/reactive-utilities/untrack) diff --git a/src/routes/reference/reactive-utilities/batch.mdx b/src/routes/reference/reactive-utilities/batch.mdx index 4642408f8..4c3eeae72 100644 --- a/src/routes/reference/reactive-utilities/batch.mdx +++ b/src/routes/reference/reactive-utilities/batch.mdx @@ -15,30 +15,66 @@ description: >- recalculations. Essential for optimizing complex state changes. --- +`batch` groups multiple reactive updates so downstream computations run once after the batch completes instead of after each individual update. + +## Import + ```ts import { batch } from "solid-js"; +``` +## Type + +```ts function batch(fn: () => T): T; ``` -`batch` is a low-level API that batches updates together. -More precisely, `batch(fn)` holds the execution of downstream computations during the `fn` block, executing them all together once the block `fn` returns. -Thus, instead of a downstream computation executing after every dependency update, it will update just once at the end of the batch. +## Parameters + +### `fn` + +- **Type:** `() => T` +- **Required:** Yes + +Function executed inside the batch. + +## Return value + +- **Type:** `T` + +Returns the value produced by `fn`. + +## Behavior -Batching improves performance by avoiding unnecessary recalculation. -Suppose you have a downstream memo `down` that depends on multiple upstream signals `up1`, `up2`, and `up3`: +- Downstream computations are deferred until the batch completes. +- Nested `batch` calls behave like a single larger batch. +- If you read a stale memo or signal inside the batch, Solid updates it on demand before returning the value. +- If `fn` is asynchronous, only the updates before the first `await` are batched. + +## Automatic batching + +Solid automatically batches updates in several cases, including: + +- inside [`createEffect`](/reference/basic-reactivity/create-effect) and [`onMount`](/reference/lifecycle/on-mount) +- inside the setter returned by [`createStore`](/reference/store-utilities/create-store) +- inside array mutation methods on [`createMutable`](/reference/store-utilities/create-mutable) + +## Examples + +### Batch multiple signal updates ```ts -import { createSignal, createMemo, createEffect } from "solid-js"; +import { batch, createEffect, createMemo, createSignal } from "solid-js"; + const [up1, setUp1] = createSignal(1); const [up2, setUp2] = createSignal(2); const [up3, setUp3] = createSignal(3); + const down = createMemo(() => up1() + up2() + up3()); -// For illustration, monitor when `down` gets recomputed: createEffect(() => console.log(down())); // outputs 6 ``` -If you directly update all of the upstream signals outside of batch mode, then `down` will recompute every time. +Without `batch`, each setter can trigger downstream work: ```ts setUp1(4); // recomputes down, outputs 9 @@ -46,7 +82,7 @@ setUp2(5); // recomputes down, outputs 12 setUp3(6); // recomputes down, outputs 15 ``` -If instead you update the upstream signals within a `batch`, then `down` will update only once at the end: +With `batch`, the memo recomputes once at the end: ```ts batch(() => { @@ -56,21 +92,7 @@ batch(() => { }); // recomputes down, outputs 30 ``` -The impact is even more dramatic if you have _m_ downstream computations (memos, effects, etc.) that each depends on _n_ upstream signals. -Without batching, modifying all _n_ upstream signals would cause _m n_ updates to the downstream computations. -With batching, modifying all _n_ upstream signals would cause _m_ updates to the downstream computations. -Given that each update takes at least _n_ time (just to read the upstream signals), this cost savings can be significant. -Batching is also especially helpful when the downstream effects include DOM updates, which can be expensive. - -Solid uses `batch` internally to automatically batch updates for you in a few cases: - -- Within [`createEffect`](/reference/basic-reactivity/create-effect) and [`onMount`](/reference/lifecycle/on-mount) (unless they are outside a [root](/reference/reactive-utilities/create-root)) -- Within the [setter of a store](/reference/store-utilities/create-store#setter) (which can update several properties at once) -- Within array methods (e.g. `Array.prototype.splice`) of a [mutable store](/reference/store-utilities/create-mutable) (which can update several elements at once) - -These save you from having to use `batch` yourself in many cases. -For the most part, automatic batching should be transparent to you, because accessing a signal or memo will cause it to update if it is out of date (as of Solid 1.4). -For example: +### Read a memo inside a batch ```ts batch(() => { @@ -84,6 +106,8 @@ batch(() => { }); // recomputes down, outputs 36 ``` -You can think of `batch(fn)` as setting a global "batch mode" variable, calling the function `fn`, and then restoring the global variable to its previous value. -This means that you can nest `batch` calls, and they will form one big batch. -It also means that, if `fn` is asynchronous, only the updates before the first `await` will be batched. +## Related + +- [`createEffect`](/reference/basic-reactivity/create-effect) +- [`createStore`](/reference/store-utilities/create-store) +- [`createMutable`](/reference/store-utilities/create-mutable) diff --git a/src/routes/reference/reactive-utilities/untrack.mdx b/src/routes/reference/reactive-utilities/untrack.mdx index 2b38a9383..1f819d5d9 100644 --- a/src/routes/reference/reactive-utilities/untrack.mdx +++ b/src/routes/reference/reactive-utilities/untrack.mdx @@ -16,9 +16,46 @@ description: >- by excluding non-updating props from reactive tracking scope. --- -Ignores tracking any of the dependencies in the executing code block and returns the value. This helper is useful when a certain `prop` will never update and thus it is ok to use it outside of the tracking scope. +`untrack` executes a function without collecting dependencies from the current reactive scope. -```tsx title="component.tsx" +## Import + +```ts +import { untrack } from "solid-js"; +``` + +## Type + +```ts +function untrack(fn: () => T): T; +``` + +## Parameters + +### `fn` + +- **Type:** `() => T` +- **Required:** Yes + +Function executed outside the current tracking context. + +## Return value + +- **Type:** `T` + +Returns the value produced by `fn`. + +## Behavior + +- Signals read inside `untrack` do not become dependencies of the surrounding computation. +- `untrack` does not disable reactivity globally; it only affects reads inside the provided function. +- Surrounding effects and memos do not subscribe to values read only inside `untrack`. + +## Examples + +### Read a prop without tracking + +```tsx import { untrack } from "solid-js"; export function Component(props) { @@ -28,12 +65,32 @@ export function Component(props) { } ``` -## Initial and Default Values +### Avoid tracking part of an effect -It is not necessary to manually untrack values that are suppose to serve as a default or initial value to a signal. Even with the linter configured to enforce tracking, the linter will accept it when a `prop` is prefixed with `default` or `initial` as it is a common pattern to use them as such. +```tsx +import { createEffect, createSignal, untrack } from "solid-js"; + +function Example() { + const [count, setCount] = createSignal(0); + const [label] = createSignal("count"); + + createEffect(() => { + console.log( + untrack(() => label()), + count() + ); + }); + + return ; +} +``` + +## Notes + +Default and initial prop values can be read directly when initializing a signal. +This pattern commonly appears with names such as `initialName` and `defaultName`. ```tsx tab title="initialValue" {5} -// component.tsx import { createSignal } from "solid-js"; export function Component(props) { @@ -44,7 +101,6 @@ export function Component(props) { ``` ```tsx tab title="defaultValue" {5} -// component.tsx import { createSignal } from "solid-js"; export function Component(props) { @@ -53,3 +109,9 @@ export function Component(props) { return
{name()}
; } ``` + +## Related + +- [`onMount`](/reference/lifecycle/on-mount) +- [`createEffect`](/reference/basic-reactivity/create-effect) +- [`on`](/reference/reactive-utilities/on-util) From 80c4b9daf7b629a7aba208c0ea538617803dce2b Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 12:19:29 -0700 Subject: [PATCH 02/88] docs: update descriptions for batch, catchError, getOwner, onCleanup, onMount, runWithOwner, and untrack --- src/routes/reference/lifecycle/on-cleanup.mdx | 4 +- src/routes/reference/lifecycle/on-mount.mdx | 4 +- .../reference/reactive-utilities/batch.mdx | 4 +- .../reactive-utilities/catch-error.mdx | 79 ++++++++++++++++--- .../reactive-utilities/get-owner.mdx | 60 +++++++++++--- .../reactive-utilities/run-with-owner.mdx | 73 ++++++++++++----- .../reference/reactive-utilities/untrack.mdx | 4 +- 7 files changed, 176 insertions(+), 52 deletions(-) diff --git a/src/routes/reference/lifecycle/on-cleanup.mdx b/src/routes/reference/lifecycle/on-cleanup.mdx index e8ae07337..79ee80fe0 100644 --- a/src/routes/reference/lifecycle/on-cleanup.mdx +++ b/src/routes/reference/lifecycle/on-cleanup.mdx @@ -12,8 +12,8 @@ tags: - disposal version: "1.0" description: >- - Register cleanup methods in SolidJS to prevent memory leaks. Executes when - components unmount or tracking scopes dispose. Essential for proper cleanup. + Register a cleanup function on the current reactive scope. The cleanup runs + when the scope is disposed or refreshed. --- `onCleanup` registers a cleanup function on the current reactive scope. diff --git a/src/routes/reference/lifecycle/on-mount.mdx b/src/routes/reference/lifecycle/on-mount.mdx index ce3f27ef9..fe5009af3 100644 --- a/src/routes/reference/lifecycle/on-mount.mdx +++ b/src/routes/reference/lifecycle/on-mount.mdx @@ -12,8 +12,8 @@ tags: - initialization version: "1.0" description: >- - Run code after SolidJS components mount to the DOM. Perfect for accessing - refs, setting up third-party libraries, and one-time initializations. + Register a function that runs once after the initial render of the current + component or root. --- `onMount` registers a function that runs once after the initial render for the current component or root. diff --git a/src/routes/reference/reactive-utilities/batch.mdx b/src/routes/reference/reactive-utilities/batch.mdx index 4c3eeae72..2d1e8e54c 100644 --- a/src/routes/reference/reactive-utilities/batch.mdx +++ b/src/routes/reference/reactive-utilities/batch.mdx @@ -11,8 +11,8 @@ tags: - updates version: "1.0" description: >- - Batch multiple signal updates in SolidJS to improve performance by reducing - recalculations. Essential for optimizing complex state changes. + Group reactive updates so downstream computations run after the batch + completes. --- `batch` groups multiple reactive updates so downstream computations run once after the batch completes instead of after each individual update. diff --git a/src/routes/reference/reactive-utilities/catch-error.mdx b/src/routes/reference/reactive-utilities/catch-error.mdx index 6d5363792..4a500a6e2 100644 --- a/src/routes/reference/reactive-utilities/catch-error.mdx +++ b/src/routes/reference/reactive-utilities/catch-error.mdx @@ -10,21 +10,80 @@ tags: - handlers version: "1.0" description: >- - Wrap SolidJS code with error handlers to catch and handle exceptions - gracefully. Create error boundaries for robust error management in components. + Establish an error boundary for work performed inside the provided function. --- -:::note +`catchError` establishes an error boundary for work performed inside the provided function. -New in v1.7.0 -::: +## Import -```tsx +```ts import { catchError } from "solid-js"; +``` + +## Type + +```ts +function catchError( + fn: () => T, + handler: (err: Error) => void +): T | undefined; +``` + +## Parameters + +### `fn` + +- **Type:** `() => T` +- **Required:** Yes + +Function executed inside the error boundary. + +### `handler` + +- **Type:** `(err: Error) => void` +- **Required:** Yes + +Error handler invoked when an error is thrown inside descendant scopes. -function catchError(tryFn: () => T, onError: (err: any) => void): T; +## Return value + +- **Type:** `T | undefined` + +Returns the value produced by `fn`. +If `handler` handles a thrown error, the result is `undefined`. + +## Behavior + +- Only the nearest matching error boundary handles a thrown error. +- If `handler` throws, the next parent error boundary handles that error. + +## Examples + +### Handle an error in a child scope + +```tsx +import { catchError, createEffect, createSignal } from "solid-js"; + +function Example() { + const [count, setCount] = createSignal(0); + + catchError( + () => { + createEffect(() => { + if (count() > 2) throw new Error("count too large"); + }); + }, + (err) => { + console.error(err.message); + } + ); + + return ; +} ``` -Wraps a `tryFn` with an error handler that fires if an error occurs below that point. -Only the nearest scope error handlers execute. -Rethrow to trigger up the line. +## Related + +- [`ErrorBoundary`](/reference/components/error-boundary) +- [`createEffect`](/reference/basic-reactivity/create-effect) diff --git a/src/routes/reference/reactive-utilities/get-owner.mdx b/src/routes/reference/reactive-utilities/get-owner.mdx index 0825246ca..f4e21e3ab 100644 --- a/src/routes/reference/reactive-utilities/get-owner.mdx +++ b/src/routes/reference/reactive-utilities/get-owner.mdx @@ -11,26 +11,60 @@ tags: - debugging version: "1.0" description: >- - Access the current tracking scope owner in SolidJS for advanced control over - cleanup and disposal. Essential for custom reactive primitives. + Return the owner for the currently executing reactive scope. --- -```tsx +`getOwner` returns the owner for the currently executing reactive scope. + +## Import + +```ts import { getOwner } from "solid-js"; import type { Owner } from "solid-js"; +``` + +## Type -function getOwner(): Owner; +```ts +function getOwner(): Owner | null; ``` -Gets the tracking scope that owns the currently running code, e.g., for passing into a later call to `runWithOwner` outside of the current scope. +## Parameters + +`getOwner` does not take any parameters. + +## Return value + +- **Type:** `Owner | null` + +Returns the current owner or `null` when no owner is active. -Internally, computations (effects, memos, etc.) create owners which are children of their owner, all the way up to the root owner created by `createRoot` or `render`. -In particular, this ownership tree lets Solid automatically clean up a disposed computation by traversing its subtree and calling all `onCleanup` callbacks. -For example, when a createEffect's dependencies change, the effect calls all descendant `onCleanup` callbacks before running the effect function again. -Calling `getOwner` returns the current owner node that is responsible for disposal of the current execution block. +## Behavior + +- Owners determine cleanup and context lookup for descendant computations. +- A computation created inside the current scope becomes part of the current owner tree unless ownership is overridden. +- Components are not computations and do not create owner nodes themselves. +- Calling `getOwner` inside component code returns the owner responsible for rendering and disposing that component subtree. +- Turning off tracking with [`untrack`](/reference/reactive-utilities/untrack) does not create a new owner. + +## Examples + +### Capture an owner for later use + +```ts +import { getOwner, runWithOwner } from "solid-js"; + +const owner = getOwner(); + +queueMicrotask(() => { + runWithOwner(owner, () => { + console.log("owner restored"); + }); +}); +``` -Components are not computations, so do not create an owner node, but they are typically rendered from a `createEffect` which does, so the result is similar: when a component gets unmounted, all descendant `onCleanup` callbacks get called. -Calling `getOwner` from a component scope returns the owner that is responsible for rendering and unmounting that component. +## Related -Note that the owning tracking scope isn't necessarily tracking. -For example, untrack turns off tracking for the duration of a function (without creating a new tracking scope), as do components created via JSX (``). +- [`runWithOwner`](/reference/reactive-utilities/run-with-owner) +- [`onCleanup`](/reference/lifecycle/on-cleanup) +- [`untrack`](/reference/reactive-utilities/untrack) diff --git a/src/routes/reference/reactive-utilities/run-with-owner.mdx b/src/routes/reference/reactive-utilities/run-with-owner.mdx index 349243cda..ff728da81 100644 --- a/src/routes/reference/reactive-utilities/run-with-owner.mdx +++ b/src/routes/reference/reactive-utilities/run-with-owner.mdx @@ -13,45 +13,76 @@ tags: - effects version: "1.0" description: >- - Execute code under a specific owner in SolidJS for proper cleanup and context - access, especially in async operations and setTimeout callbacks. + Execute a function under the provided owner. --- +`runWithOwner` executes a function under the provided owner. + +## Import + ```ts -import { runWithOwner } from "solid-js" -import type { Owner } from "solid-js" +import { runWithOwner } from "solid-js"; +import type { Owner } from "solid-js"; +``` -function runWithOwner(owner: Owner, fn: (() => void) => T): T +## Type + +```ts +function runWithOwner(owner: Owner | null, fn: () => T): T | undefined; ``` -Executes the given function under the provided owner, instead of (and without affecting) the owner of the outer scope. -By default, computations created by `createEffect`, `createMemo`, etc. are owned by the owner of the currently executing code (the return value of `getOwner`), so in particular these will get disposed when their owner does. -Calling `runWithOwner` provides a way to override this default to a manually specified owner (typically, the return value from a previous call to `getOwner`), enabling more precise control of when computations get disposed. +## Parameters + +### `owner` + +- **Type:** `Owner | null` +- **Required:** Yes + +Owner used while executing `fn`. + +### `fn` -Having a (correct) owner is important for two reasons: +- **Type:** `() => T` +- **Required:** Yes -- Computations without an owner cannot be cleaned up. - For example, if you call `createEffect` without an owner (e.g., in the global scope), the effect will continue running forever, instead of being disposed when its owner gets disposed. +Function executed under `owner`. -- `useContext` obtains context by walking up the owner tree to find the nearest ancestor providing the desired context. - So without an owner you cannot look up any provided context (and with the wrong owner, you might obtain the wrong context). +## Return value -Manually setting the owner is especially helpful when doing reactivity outside of any owner scope. -In particular, asynchronous computation (via either `async` functions or callbacks like `setTimeout`) lose their automatically set owner, so remembering the original owner via `getOwner` and restoring it via `runWithOwner` is necessary in these cases. -For example: +- **Type:** `T | undefined` + +Returns the value produced by `fn`. + +## Behavior + +- Computations created inside `fn` are attached to the provided owner. +- Context lookup inside `fn` walks the provided owner tree. +- Ownership affects cleanup and context resolution. +- Ownership does not restore dependency tracking across asynchronous boundaries. +- Code after the first `await` in an `async` function runs without reactive dependency tracking. + +## Examples + +### Restore an owner in a callback ```ts +import { createEffect, getOwner, runWithOwner, useContext } from "solid-js"; + const owner = getOwner(); + setTimeout(() => { - // This callback gets run without owner. - // Restore owner via runWithOwner: runWithOwner(owner, () => { - const foo = useContext(FooContext); + const value = useContext(FooContext); + createEffect(() => { - console.log(foo); + console.log(value); }); }); }, 1000); ``` -**Note:** that owners are not what determines dependency tracking, so `runWithOwner` does not help with tracking in asynchronous functions; use of reactive state in the asynchronous part (e.g. after the first `await`) will not be tracked as a dependency. +## Related + +- [`getOwner`](/reference/reactive-utilities/get-owner) +- [`useContext`](/reference/component-apis/use-context) +- [`onCleanup`](/reference/lifecycle/on-cleanup) diff --git a/src/routes/reference/reactive-utilities/untrack.mdx b/src/routes/reference/reactive-utilities/untrack.mdx index 1f819d5d9..270c71f0c 100644 --- a/src/routes/reference/reactive-utilities/untrack.mdx +++ b/src/routes/reference/reactive-utilities/untrack.mdx @@ -12,8 +12,8 @@ tags: - defaults version: "1.0" description: >- - Prevent dependency tracking for static values in SolidJS. Optimize performance - by excluding non-updating props from reactive tracking scope. + Execute a function without collecting dependencies from the current reactive + scope. --- `untrack` executes a function without collecting dependencies from the current reactive scope. From da1b83ee41d78ce715508cf918823f8ce7eeb37a Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 12:22:47 -0700 Subject: [PATCH 03/88] docs: update documentation for createReaction, startTransition, and useTransition --- .../reactive-utilities/start-transition.mdx | 62 ++++++++++++++-- .../reactive-utilities/use-transition.mdx | 66 ++++++++++++++--- .../secondary-primitives/create-reaction.mdx | 73 ++++++++++++++++--- 3 files changed, 173 insertions(+), 28 deletions(-) diff --git a/src/routes/reference/reactive-utilities/start-transition.mdx b/src/routes/reference/reactive-utilities/start-transition.mdx index b628412ea..92a27daf2 100644 --- a/src/routes/reference/reactive-utilities/start-transition.mdx +++ b/src/routes/reference/reactive-utilities/start-transition.mdx @@ -11,16 +11,66 @@ tags: - batching version: "1.0" description: >- - Start transitions in SolidJS without pending state tracking. Batch async - updates for improved performance and smoother user interactions. + Start a transition and return a promise that resolves when the transition + completes. --- +`startTransition` starts a transition without exposing a pending-state accessor. + +## Import + ```ts -import { startTransition } from "solid-js" +import { startTransition } from "solid-js"; +``` -function startTransition: (fn: () => void) => Promise +## Type +```ts +function startTransition(fn: () => unknown): Promise; ``` -Similar to `useTransition` except there is no associated pending state. -This one can just be used directly to start the Transition. +## Parameters + +### `fn` + +- **Type:** `() => unknown` +- **Required:** Yes + +Function executed inside the transition. + +## Return value + +- **Type:** `Promise` + +Resolves when the transition completes. + +## Behavior + +- Updates scheduled inside `fn` run as a transition. +- The returned promise resolves after transition work completes. +- This API does not provide a pending-state accessor. + +## Examples + +### Start a transition + +```tsx +import { createSignal, startTransition } from "solid-js"; + +function Example() { + const [value, setValue] = createSignal(""); + + async function update(next: string) { + await startTransition(() => { + setValue(next); + }); + } + + return ; +} +``` + +## Related + +- [`useTransition`](/reference/reactive-utilities/use-transition) +- [`Suspense`](/reference/components/suspense) diff --git a/src/routes/reference/reactive-utilities/use-transition.mdx b/src/routes/reference/reactive-utilities/use-transition.mdx index 71ae753d8..894d633de 100644 --- a/src/routes/reference/reactive-utilities/use-transition.mdx +++ b/src/routes/reference/reactive-utilities/use-transition.mdx @@ -12,28 +12,72 @@ tags: - concurrent version: "1.0" description: >- - Batch async updates with transitions in SolidJS. Track pending states and - defer commits until all async processes complete for smooth UI updates. + Return a pending-state accessor and a function that starts a transition. --- +`useTransition` returns a pending-state accessor and a function that starts a transition. + +## Import + ```ts import { useTransition } from "solid-js"; +``` +## Type + +```ts function useTransition(): [ pending: () => boolean, - startTransition: (fn: () => void) => Promise, + start: (fn: () => void) => Promise, ]; ``` -Used to batch async updates in a transaction deferring commit until all async processes are complete. -This is tied into Suspense and only tracks resources read under Suspense boundaries. +## Parameters -```ts -const [isPending, start] = useTransition(); +`useTransition` does not take any parameters. + +## Return value + +- **Type:** `[() => boolean, (fn: () => void) => Promise]` + +Returns a tuple containing: -// check if transitioning -isPending(); +- `pending`: an accessor that reports whether the transition is pending +- `start`: a function that starts a transition -// wrap in transition -start(() => setSignal(newValue), () => /* transition is done */) +## Behavior + +- `start` schedules updates inside a transition. +- `pending()` is `true` while the transition is pending. +- Transition state integrates with Suspense and resource reads under Suspense boundaries. + +## Examples + +### Read transition state + +```tsx +import { createSignal, useTransition } from "solid-js"; + +function Example() { + const [value, setValue] = createSignal(""); + const [pending, start] = useTransition(); + + return ( + <> + +
{pending() ? "pending" : value()}
+ + ); +} ``` + +## Related + +- [`startTransition`](/reference/reactive-utilities/start-transition) +- [`Suspense`](/reference/components/suspense) diff --git a/src/routes/reference/secondary-primitives/create-reaction.mdx b/src/routes/reference/secondary-primitives/create-reaction.mdx index b29ccf7b9..0ef6c6dea 100644 --- a/src/routes/reference/secondary-primitives/create-reaction.mdx +++ b/src/routes/reference/secondary-primitives/create-reaction.mdx @@ -11,28 +11,79 @@ tags: - advanced version: "1.0" description: >- - Separate tracking from execution with createReaction. Create one-time reactive - side effects that run on first dependency change only. + Create a reaction that runs once when the tracked expression is invalidated. --- +`createReaction` creates a reaction that invalidates once for each call to the returned tracking function. + +## Import + ```ts import { createReaction } from "solid-js"; +``` -function createReaction(onInvalidate: () => void): (fn: () => void) => void; +## Type + +```ts +function createReaction( + onInvalidate: () => void, + options?: { name?: string } +): (tracking: () => void) => void; ``` -Sometimes it is useful to separate tracking from re-execution. -This primitive registers a side effect that is run the first time the expression wrapped by the returned tracking function is notified of a change. +## Parameters + +### `onInvalidate` + +- **Type:** `() => void` +- **Required:** Yes + +Callback invoked when the tracked computation is invalidated. + +### `options` + +- **Type:** `{ name?: string }` +- **Required:** No + +Optional configuration object. + +#### `name` + +- **Type:** `string` +- **Required:** No + +Debug name used by development tooling. + +## Return value + +- **Type:** `(tracking: () => void) => void` + +Returns a function that executes `tracking` and records its dependencies for a single invalidation. + +## Behavior + +- The returned tracking function records dependencies read during `tracking`. +- `onInvalidate` runs on the first change to any recorded dependency. +- After invalidation, the reaction does not run again until the tracking function is called again. + +## Examples + +### Track a signal for one invalidation ```ts -const [s, set] = createSignal("start"); +import { createReaction, createSignal } from "solid-js"; -const track = createReaction(() => console.log("something")); +const [value, setValue] = createSignal("start"); -// run the reaction next time `s` changes. -track(() => s()); +const track = createReaction(() => console.log("changed")); -set("end"); // "something" +track(() => value()); -set("final"); // no-op since the reaction only runs on the first update, need to call `track` again. +setValue("end"); // logs "changed" +setValue("final"); // no-op until track() runs again ``` + +## Related + +- [`createEffect`](/reference/basic-reactivity/create-effect) +- [`createComputed`](/reference/secondary-primitives/create-computed) From d448223381d3ce5caab9158ced9993575d2f29ae Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 12:33:00 -0700 Subject: [PATCH 04/88] docs: update documentation for from, indexArray, mapArray, and observable --- .../reference/reactive-utilities/from.mdx | 88 +++++++++++---- .../reactive-utilities/index-array.mdx | 106 +++++++++++++----- .../reactive-utilities/map-array.mdx | 103 ++++++++++++----- .../reactive-utilities/observable.mdx | 50 +++++++-- 4 files changed, 255 insertions(+), 92 deletions(-) diff --git a/src/routes/reference/reactive-utilities/from.mdx b/src/routes/reference/reactive-utilities/from.mdx index 1fe6be429..306d7a3d4 100644 --- a/src/routes/reference/reactive-utilities/from.mdx +++ b/src/routes/reference/reactive-utilities/from.mdx @@ -11,45 +11,85 @@ tags: - integration version: "1.0" description: >- - Convert RxJS observables and external producers into SolidJS signals. Seamless - integration with third-party reactive libraries and state managers. + Convert an external producer or subscribable into a Solid accessor. --- -```tsx +`from` creates an accessor from an external producer or subscribable source. + +## Import + +```ts import { from } from "solid-js"; +``` -function from( - producer: - | ((setter: (v: T) => T) => () => void) - | { - subscribe: ( - fn: (v: T) => void - ) => (() => void) | { unsubscribe: () => void }; - } -): () => T | undefined; +## Type + +```ts +function from(producer: Producer, initialValue: T): () => T; +function from(producer: Producer): () => T | undefined; + +type Producer = + | ((setter: Setter) => () => void) + | { + subscribe: ( + fn: (value: T) => void + ) => (() => void) | { unsubscribe: () => void }; + }; ``` -A helper to make it easier to interop with external producers like RxJS observables or with Svelte Stores. -This basically turns any subscribable (object with a subscribe method) into a Signal and manages subscription and disposal. +## Parameters + +### `producer` + +- **Type:** `Producer` or `Producer` +- **Required:** Yes + +Producer function or subscribable object. + +### `initialValue` + +- **Type:** `T` +- **Required:** No + +Initial value for the accessor. -```tsx -const signal = from(obsv$); +## Return value + +- **Type:** `() => T` or `() => T | undefined` + +Returns an accessor backed by the external source. + +## Behavior + +- A producer function receives a Solid setter and returns a cleanup function. +- A subscribable source must expose `subscribe()`. +- Returned subscriptions are cleaned up with the owning Solid scope. +- When `initialValue` is omitted, the accessor can return `undefined`. + +## Examples + +### Create an accessor from a subscribable source + +```ts +import { from } from "solid-js"; + +const signal = from(observableSource); ``` -It can also take a custom producer function where the function is passed a setter function that returns an unsubscribe function: +### Create an accessor from a producer function + +```ts +import { from } from "solid-js"; -```tsx const clock = from((set) => { const interval = setInterval(() => { - set((v) => v + 1); + set((value) => value + 1); }, 1000); return () => clearInterval(interval); -}); +}, 0); ``` -## Arguments +## Related -| Name | Type | Description | -| :------- | :----------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------- | -| producer | `((setter: (v: T) => T) => () => void) \| { subscribe: (fn: (v: T) => void) => (() => void) \| { unsubscribe: () => void }; }` | The producer function or subscribable object | +- [`observable`](/reference/reactive-utilities/observable) diff --git a/src/routes/reference/reactive-utilities/index-array.mdx b/src/routes/reference/reactive-utilities/index-array.mdx index 421b836a8..5aa4959af 100644 --- a/src/routes/reference/reactive-utilities/index-array.mdx +++ b/src/routes/reference/reactive-utilities/index-array.mdx @@ -11,43 +11,91 @@ tags: - optimization version: "1.0" description: >- - Map arrays by index in SolidJS where items are signals and indices are - constant. Optimized helper for index-based list rendering patterns. + Reactively map an array by index and return an accessor for the mapped + result. --- -```tsx +`indexArray` reactively maps an array by index. + +## Import + +```ts import { indexArray } from "solid-js"; +``` + +## Type +```ts function indexArray( - list: () => readonly T[], - mapFn: (v: () => T, i: number) => U + list: () => readonly T[] | undefined | null | false, + mapFn: (value: () => T, index: number) => U, + options?: { fallback?: () => any } ): () => U[]; ``` -Similar to `mapArray` except it maps by index. -The item is a signal and the index is now the constant. - -Underlying helper for the `` control flow. - -```tsx -const mapped = indexArray(source, (model) => { - return { - get id() { - return model().id - } - get firstInitial() { - return model().firstName[0]; - }, - get fullName() { - return `${model().firstName} ${model().lastName}`; - }, - } -}); +## Parameters + +### `list` + +- **Type:** `() => readonly T[] | undefined | null | false` +- **Required:** Yes + +Accessor that returns the source array. + +### `mapFn` + +- **Type:** `(value: () => T, index: number) => U` +- **Required:** Yes + +Mapping function for each index. + +### `options` + +- **Type:** `{ fallback?: () => any }` +- **Required:** No + +Optional configuration object. + +#### `fallback` + +- **Type:** `() => any` +- **Required:** No + +Fallback accessor used when the source array is empty or falsy. + +## Return value + +- **Type:** `() => U[]` + +Returns an accessor for the mapped array. + +## Behavior + +- Items are mapped by index rather than by value identity. +- The item argument is an accessor. +- The index argument is a number. +- Updating an item at the same index updates the corresponding mapped result. +- This is the underlying helper for [``](/reference/components/index-component). + +## Examples + +### Map an array by index + +```ts +import { createSignal, indexArray } from "solid-js"; + +const [source] = createSignal([ + { firstName: "Ada", lastName: "Lovelace" }, + { firstName: "Grace", lastName: "Hopper" }, +]); + +const mapped = indexArray(source, (item, index) => ({ + index, + fullName: () => `${item().firstName} ${item().lastName}`, +})); ``` -## Arguments +## Related -| Name | Type | Description | -| :---- | :----------------------------- | :-------------------- | -| list | `() => readonly T[]` | The list to map. | -| mapFn | `(v: () => T, i: number) => U` | The mapping function. | +- [``](/reference/components/index-component) +- [`mapArray`](/reference/reactive-utilities/map-array) diff --git a/src/routes/reference/reactive-utilities/map-array.mdx b/src/routes/reference/reactive-utilities/map-array.mdx index 80883a7c6..1dd563f67 100644 --- a/src/routes/reference/reactive-utilities/map-array.mdx +++ b/src/routes/reference/reactive-utilities/map-array.mdx @@ -11,47 +11,92 @@ tags: - optimization version: "1.0" description: >- - Efficiently map reactive arrays in SolidJS with cached transformations. - Reduces unnecessary re-renders by tracking items by reference. + Reactively map an array by item identity and return an accessor for the mapped + result. --- +`mapArray` reactively maps an array and caches mapped items by value identity. + +## Import + ```ts import { mapArray } from "solid-js"; +``` + +## Type +```ts function mapArray( - list: () => readonly T[], - mapFn: (v: T, i: () => number) => U + list: () => readonly T[] | undefined | null | false, + mapFn: (value: T, index: () => number) => U, + options?: { fallback?: () => any } ): () => U[]; ``` -Reactive map helper that caches each item by reference to reduce unnecessary mapping on updates. -It only runs the mapping function once per value and then moves or removes it as needed. -The index argument is a signal. The map function itself is not tracking. +## Parameters + +### `list` + +- **Type:** `() => readonly T[] | undefined | null | false` +- **Required:** Yes + +Accessor that returns the source array. + +### `mapFn` + +- **Type:** `(value: T, index: () => number) => U` +- **Required:** Yes + +Mapping function for each item. + +### `options` + +- **Type:** `{ fallback?: () => any }` +- **Required:** No -Underlying helper for the `` control flow. +Optional configuration object. + +#### `fallback` + +- **Type:** `() => any` +- **Required:** No + +Fallback accessor used when the source array is empty or falsy. + +## Return value + +- **Type:** `() => U[]` + +Returns an accessor for the mapped array. + +## Behavior + +- Items are cached by value identity. +- The index argument is an accessor. +- `mapFn` is not a tracking scope. +- Reordering reuses existing mapped items when their source values are retained. +- This is the underlying helper for [``](/reference/components/for). + +## Examples + +### Map an array with cached items ```ts -const mapped = mapArray(source, (model) => { - const [name, setName] = createSignal(model.name); - const [description, setDescription] = createSignal(model.description); - - return { - id: model.id, - get name() { - return name(); - }, - get description() { - return description(); - }, - setName, - setDescription, - }; -}); +import { createSignal, mapArray } from "solid-js"; + +const [source] = createSignal([ + { id: 1, name: "A" }, + { id: 2, name: "B" }, +]); + +const mapped = mapArray(source, (item, index) => ({ + id: item.id, + name: item.name, + position: index, +})); ``` -## Arguments +## Related -| Name | Type | Description | -| :---- | :----------------------------- | :----------------------- | -| list | `() => readonly T[]` | The source array to map. | -| mapFn | `(v: T, i: () => number) => U` | The mapping function. | +- [``](/reference/components/for) +- [`indexArray`](/reference/reactive-utilities/index-array) diff --git a/src/routes/reference/reactive-utilities/observable.mdx b/src/routes/reference/reactive-utilities/observable.mdx index e879bae13..6992d6110 100644 --- a/src/routes/reference/reactive-utilities/observable.mdx +++ b/src/routes/reference/reactive-utilities/observable.mdx @@ -12,29 +12,59 @@ tags: - streams version: "1.0" description: >- - Convert SolidJS signals to Observables for seamless integration with RxJS and - other reactive libraries using the observable utility function. + Convert a Solid accessor into an Observable-compatible object. --- +`observable` creates an Observable-compatible object from a Solid accessor. + +## Import + ```ts import { observable } from "solid-js"; +``` +## Type + +```ts function observable(input: () => T): Observable; ``` -This method takes a signal and produces an Observable. -You can consume it from another Observable library of your choice, typically with the `from` operator. +## Parameters + +### `input` + +- **Type:** `() => T` +- **Required:** Yes + +Accessor used as the observable source. + +## Return value + +- **Type:** `Observable` + +Returns an object with `subscribe()` and `[Symbol.observable]()`. + +## Behavior + +- The returned object is compatible with Observable interop based on `Symbol.observable`. +- Subscriptions receive values from the accessor. +- Observable libraries can consume the returned object through standard Observable interop. + +## Examples + +### Convert an accessor to an Observable-compatible source ```ts -// How to integrate rxjs with a Solid signal -import { observable } from "solid-js"; +import { createSignal, observable } from "solid-js"; import { from } from "rxjs"; -const [s, set] = createSignal(0); +const [value] = createSignal(0); -const obsv$ = from(observable(s)); +const value$ = from(observable(value)); -obsv$.subscribe((v) => console.log(v)); +value$.subscribe((next) => console.log(next)); ``` -You can also use `from` without rxjs; check out this [page](/reference/reactive-utilities/from). +## Related + +- [`from`](/reference/reactive-utilities/from) From c4de20b82d4736827f4356cb4ff89db5a5edd6cb Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 13:08:42 -0700 Subject: [PATCH 05/88] docs: refine documentation for children, createContext, useContext, and related APIs --- .../reference/component-apis/children.mdx | 61 ++----- .../component-apis/create-context.mdx | 153 +++++++++--------- .../reference/component-apis/use-context.mdx | 85 +++++++--- .../reactive-utilities/get-owner.mdx | 3 +- .../reactive-utilities/run-with-owner.mdx | 3 +- .../reactive-utilities/start-transition.mdx | 2 +- .../reactive-utilities/use-transition.mdx | 2 +- 7 files changed, 165 insertions(+), 144 deletions(-) diff --git a/src/routes/reference/component-apis/children.mdx b/src/routes/reference/component-apis/children.mdx index d4f79c895..45b2a80c5 100644 --- a/src/routes/reference/component-apis/children.mdx +++ b/src/routes/reference/component-apis/children.mdx @@ -10,13 +10,10 @@ tags: - utilities version: "1.0" description: >- - Resolve and normalize a component's `children` prop into a stable accessor. - Useful when you need to read or transform children imperatively inside a - component. + Resolve a component's `children` prop into a stable accessor. --- -`children` normalizes a component's `children` prop into a stable accessor that returns resolved JSX elements. -It accepts functions, arrays, fragments, and nested structures. +`children` resolves a component's `children` prop and returns an accessor for the resolved result. ## Import @@ -41,46 +38,40 @@ type ChildrenReturn = Accessor & { - **Type:** `() => JSX.Element` - **Required:** Yes -An accessor that returns the `children` value (typically `props.children`). +Accessor that returns the component's `children` value. ## Return value - **Type:** `ChildrenReturn` -The function returns a callable accessor. -Calling it yields the resolved children, either a single element or an array. +Returns an accessor for the resolved children. +The accessor also exposes `toArray()`. -## Helpers +## Behavior -### `toArray()` - -- **Type:** `() => ResolvedChildren[]` - -- **Description:** Returns a flattened array of resolved child elements. - -This method is exposed on the returned accessor and is useful for iteration or index-based logic. +- `children` resolves functions, arrays, fragments, and nested child structures. +- The returned accessor reads the current resolved value of `props.children`. +- `toArray()` returns a flattened array of resolved child elements. ## Examples ### Basic usage ```tsx +import { children } from "solid-js"; + function Wrapper(props) { const resolved = children(() => props.children); return
{resolved()}
; } - -// Usage - - one - two -; ``` -### `.toArray()` example +### Flatten children into an array ```tsx +import { children } from "solid-js"; + function List(props) { const resolved = children(() => props.children); const items = resolved.toArray(); @@ -93,28 +84,8 @@ function List(props) { ); } - -// Usage - - one - two -; ``` -> [!NOTE] -> `children` resolves the current value of `props.children`. -> If `props.children` is reactive, the resolved accessor reflects updates. +## Related -### Working with function-as-children - -If `children` is a function, the helper evaluates it and returns its rendered result. - -```tsx -function Slot(props) { - const resolved = children(() => props.children); - return
{resolved()}
; -} - -// Usage -{() => dynamic}; -``` +- [`createContext`](/reference/component-apis/create-context) diff --git a/src/routes/reference/component-apis/create-context.mdx b/src/routes/reference/component-apis/create-context.mdx index 37070da49..382fb3f28 100644 --- a/src/routes/reference/component-apis/create-context.mdx +++ b/src/routes/reference/component-apis/create-context.mdx @@ -12,103 +12,104 @@ tags: - dependency-injection version: "1.0" description: >- - Create context providers with createContext to share data across components - without prop drilling. Perfect for themes, auth, and global app state. + Create a context object with a `Provider` component and a default value. --- -Context provides a form of dependency injection in Solid. -It is used to save from needing to pass data as props through intermediate components (aka **prop drilling**). -This function creates a new context object that can be used with [useContext](/reference/component-apis/use-context) and offers the Provider control flow. -The default value is used when no Provider is found above in the hierarchy. +`createContext` creates a context object. +The returned object contains a `Provider` component and a `defaultValue`; [`useContext`](/reference/component-apis/use-context) reads the nearest matching provider value. -## Usage +## Import -To avoid reinstatiating a new context when Hot-Module Replacement (HMR) occurs, it is recommended to use `createContext` in its own module (file). +```ts +import { createContext } from "solid-js"; +``` + +## Type + +```ts +interface Context { + id: symbol; + Provider: (props: { value: T; children: any }) => any; + defaultValue: T; +} -:::danger[Hot-Module Replacement] -When using HMR, the context is lost when the module is reloaded. -Which will cause an error to be thrown as `useContext` will try to access it while it is still reloading. -::: +function createContext( + defaultValue?: undefined, + options?: { name?: string } +): Context; +``` -For example: +## Parameters -```ts title="/context/counter.ts" -import { createContext } from "solid-js"; +### `defaultValue` -export const INITIAL_COUNT = 0; +- **Type:** `T` +- **Required:** No -const INITIAL_STORE_SETTER = { - increment: () => {}, - decrement: () => {}, -}; +Default value returned by [`useContext`](/reference/component-apis/use-context) when no matching provider is found. -export const CounterContext = createContext([ - { count: INITIAL_COUNT }, - INITIAL_STORE_SETTER, -]); -``` +### `options` -With the context created in its own module, you can use to instantiate the context provider. - -```ts title="/context/counter-component.tsx" -import { createStore } from 'solid-js/store'; -import { CounterContext, INITIAL_COUNT } from "./counter.ts"; - -export function CounterProvider(props) { - const [value, setValue] = createStore({ count: props.initialCount || INITIAL_COUNT }) - - const counter = [ - value, - { - increment() { - setValue("count", currentCount => currentCount + 1) - }, - decrement() { - setValue("count", currentCount => currentCount - 1) - }, - }, - ] - - return ( - - {props.children} - - ) -} -``` +- **Type:** `{ name?: string }` +- **Required:** No + +Optional configuration object. + +#### `name` -A few imporant notes on how to pass data through the context API: +- **Type:** `string` +- **Required:** No -- The value passed to provider is passed to `useContext` as is. -- Wrapping as a reactive expression will not work. -- You should pass in Signals and Stores directly instead of accessing them in the JSX. +Debug name used by development tooling. -To learn how to consume the context, see the [useContext](/reference/component-apis/use-context) documentation and the [Context concepts entry](/concepts/context). +## Return value -## Default Values +- **Type:** `Context` or `Context` -`createContext()` takes an optional "default value" as an argument. -If `useContext` is called and there is no corresponding context provider above it in the component hierarchy, then the value passed as `defaultValue` is returned. +Returns a context object containing `id`, `Provider`, and `defaultValue`. -However, if no `defaultValue` was passed, then `undefined` is returned in this case. -Also, `defaultValue` (or `undefined`) is returned if `useContext` is called inside an event callback, as it is then outside of the component hierarchy. +## Behavior -This has implications for TS. -If no `defaultValue` is passed, then it is possible that `useContext()` will return `undefined`, and the types reflect this. +- `Context.Provider` passes its `value` prop to descendant calls to [`useContext`](/reference/component-apis/use-context). +- If no matching provider is found, `useContext` returns `defaultValue` when one was supplied. +- If no matching provider is found and no default value was supplied, `useContext` returns `undefined`. +- Calling `useContext` outside the owner tree of a matching provider also returns the default value or `undefined`. -Another (used in the example in the previous section) is provide a default value to `createContext()`. -In that case, `useContext()` will always return a value, and therefore TS will not complain either. -The pitfall with this approach is that if you _unintentionally_ use `useContext` outside of a provider, it may not be immediately apparent, because the context is still providing a valid value. -Therefore, if you expect to always use `useContext` within a provider, it is best to use the error based approach described above. +## Notes -## Type signature +- During Hot Module Replacement (HMR), recreating a context in a reloaded module creates a new context object. +- If provider and consumer modules are temporarily out of sync during reload, `useContext` can read a different context object and return the default value or `undefined`. +- Defining the context in its own module keeps the exported context object stable across imports. + +## Examples + +### Create a context with a default value ```ts -interface Context { - id: symbol; - Provider: (props: { value: T; children: any }) => any; - defaultValue: T; -} +import { createContext } from "solid-js"; + +type Theme = "light" | "dark"; -function createContext(defaultValue?: T): Context; +const ThemeContext = createContext("light"); ``` + +### Create a provider + +```tsx +import { createContext } from "solid-js"; + +const CounterContext = createContext(); + +function CounterProvider(props) { + return ( + + {props.children} + + ); +} +``` + +## Related + +- [`useContext`](/reference/component-apis/use-context) +- [Context](/concepts/context) diff --git a/src/routes/reference/component-apis/use-context.mdx b/src/routes/reference/component-apis/use-context.mdx index aa8160eb1..255295d02 100644 --- a/src/routes/reference/component-apis/use-context.mdx +++ b/src/routes/reference/component-apis/use-context.mdx @@ -11,40 +11,87 @@ tags: - consumption version: "1.0" description: >- - Access context values with useContext to consume data from parent providers. - Avoid prop drilling and access shared state throughout your component tree. + Read the current value of a context object created by `createContext`. --- -Used to grab context within a context provider scope to allow for deep passing of props without having to pass them through each Component function. -It's therefore used in conjunction with [`createContext`](/reference/component-apis/create-context) to consume the data from a Provider scope and thus avoid passing data through intermediate components (prop drilling). +`useContext` returns the current value for a context object. + +## Import + +```ts +import { useContext } from "solid-js"; +``` + +## Type ```ts -const [state, { increment, decrement }] = useContext(CounterContext); +interface Context { + id: symbol; + Provider: (props: { value: T; children: any }) => any; + defaultValue: T; +} + +function useContext(context: Context): T; +``` + +## Parameters + +### `context` + +- **Type:** `Context` +- **Required:** Yes + +Context object created by [`createContext`](/reference/component-apis/create-context). + +## Return value + +- **Type:** `T` + +Returns the value provided by the nearest matching `Context.Provider`. +If no provider is found, it returns the context's default value or `undefined`. + +## Behavior + +- `useContext` reads the nearest matching provider in the current owner tree. +- When no provider is found, it returns the default value from [`createContext`](/reference/component-apis/create-context). +- When no provider is found and no default value was supplied, the result is `undefined`. +- Calling `useContext` outside the owner tree of a matching provider returns the default value or `undefined`. + +## Examples + +### Read a context value + +```tsx +import { createContext, useContext } from "solid-js"; + +const CounterContext = createContext(0); + +function CounterValue() { + const value = useContext(CounterContext); + + return {value}; +} ``` -## Recommended usage +### Throw when a provider is missing + +```ts +import { createContext, useContext } from "solid-js"; -It is often a good idea to wrap `useContext` in a function like so: +const CounterContext = createContext(); -```ts title="/context/counter-component.tsx" function useCounterContext() { const context = useContext(CounterContext); - if (!context) { - throw new Error("useCounterContext: cannot find a CounterContext"); + if (context === undefined) { + throw new Error("CounterContext is missing"); } return context; } ``` -See the API reference of [createContext](/reference/component-apis/create-context) the API on how to generate a Provider scope. -And check the [Context concepts](/concepts/context) for more information on how to architecture your contexts. - -## Type signature +## Related -```ts -import { type Context } from "solid-js"; - -function useContext(context: Context): T; -``` +- [`createContext`](/reference/component-apis/create-context) +- [Context](/concepts/context) diff --git a/src/routes/reference/reactive-utilities/get-owner.mdx b/src/routes/reference/reactive-utilities/get-owner.mdx index f4e21e3ab..11ececd8a 100644 --- a/src/routes/reference/reactive-utilities/get-owner.mdx +++ b/src/routes/reference/reactive-utilities/get-owner.mdx @@ -20,12 +20,13 @@ description: >- ```ts import { getOwner } from "solid-js"; -import type { Owner } from "solid-js"; ``` ## Type ```ts +type Owner = unknown; + function getOwner(): Owner | null; ``` diff --git a/src/routes/reference/reactive-utilities/run-with-owner.mdx b/src/routes/reference/reactive-utilities/run-with-owner.mdx index ff728da81..0cf133efa 100644 --- a/src/routes/reference/reactive-utilities/run-with-owner.mdx +++ b/src/routes/reference/reactive-utilities/run-with-owner.mdx @@ -22,12 +22,13 @@ description: >- ```ts import { runWithOwner } from "solid-js"; -import type { Owner } from "solid-js"; ``` ## Type ```ts +type Owner = unknown; + function runWithOwner(owner: Owner | null, fn: () => T): T | undefined; ``` diff --git a/src/routes/reference/reactive-utilities/start-transition.mdx b/src/routes/reference/reactive-utilities/start-transition.mdx index 92a27daf2..2f526b75f 100644 --- a/src/routes/reference/reactive-utilities/start-transition.mdx +++ b/src/routes/reference/reactive-utilities/start-transition.mdx @@ -52,7 +52,7 @@ Resolves when the transition completes. ## Examples -### Start a transition +### Basic usage ```tsx import { createSignal, startTransition } from "solid-js"; diff --git a/src/routes/reference/reactive-utilities/use-transition.mdx b/src/routes/reference/reactive-utilities/use-transition.mdx index 894d633de..749e1e605 100644 --- a/src/routes/reference/reactive-utilities/use-transition.mdx +++ b/src/routes/reference/reactive-utilities/use-transition.mdx @@ -53,7 +53,7 @@ Returns a tuple containing: ## Examples -### Read transition state +### Basic usage ```tsx import { createSignal, useTransition } from "solid-js"; From 67a8923165eb6579ef9266443877d34b2935f899 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 13:17:08 -0700 Subject: [PATCH 06/88] docs: update documentation for createContext, createDeferred, createReaction, createSelector, indexArray, and mapArray --- .../component-apis/create-context.mdx | 7 -- .../reactive-utilities/index-array.mdx | 6 - .../reactive-utilities/map-array.mdx | 6 - .../secondary-primitives/create-deferred.mdx | 92 ++++++++++++-- .../secondary-primitives/create-reaction.mdx | 6 - .../secondary-primitives/create-selector.mdx | 116 +++++++++++------- 6 files changed, 151 insertions(+), 82 deletions(-) diff --git a/src/routes/reference/component-apis/create-context.mdx b/src/routes/reference/component-apis/create-context.mdx index 382fb3f28..93fd78195 100644 --- a/src/routes/reference/component-apis/create-context.mdx +++ b/src/routes/reference/component-apis/create-context.mdx @@ -44,21 +44,14 @@ function createContext( ### `defaultValue` - **Type:** `T` -- **Required:** No Default value returned by [`useContext`](/reference/component-apis/use-context) when no matching provider is found. ### `options` -- **Type:** `{ name?: string }` -- **Required:** No - -Optional configuration object. - #### `name` - **Type:** `string` -- **Required:** No Debug name used by development tooling. diff --git a/src/routes/reference/reactive-utilities/index-array.mdx b/src/routes/reference/reactive-utilities/index-array.mdx index 5aa4959af..104209c90 100644 --- a/src/routes/reference/reactive-utilities/index-array.mdx +++ b/src/routes/reference/reactive-utilities/index-array.mdx @@ -51,15 +51,9 @@ Mapping function for each index. ### `options` -- **Type:** `{ fallback?: () => any }` -- **Required:** No - -Optional configuration object. - #### `fallback` - **Type:** `() => any` -- **Required:** No Fallback accessor used when the source array is empty or falsy. diff --git a/src/routes/reference/reactive-utilities/map-array.mdx b/src/routes/reference/reactive-utilities/map-array.mdx index 1dd563f67..1c21f4dac 100644 --- a/src/routes/reference/reactive-utilities/map-array.mdx +++ b/src/routes/reference/reactive-utilities/map-array.mdx @@ -51,15 +51,9 @@ Mapping function for each item. ### `options` -- **Type:** `{ fallback?: () => any }` -- **Required:** No - -Optional configuration object. - #### `fallback` - **Type:** `() => any` -- **Required:** No Fallback accessor used when the source array is empty or falsy. diff --git a/src/routes/reference/secondary-primitives/create-deferred.mdx b/src/routes/reference/secondary-primitives/create-deferred.mdx index 9f7debad4..980b3b7da 100644 --- a/src/routes/reference/secondary-primitives/create-deferred.mdx +++ b/src/routes/reference/secondary-primitives/create-deferred.mdx @@ -11,30 +11,98 @@ tags: - updates version: "1.0" description: >- - Defer reactive updates until browser idle with createDeferred. Optimize - performance by batching non-critical changes with timeout control. + Create a deferred accessor that updates when the browser is idle or when the + timeout is reached. --- +`createDeferred` returns a deferred accessor for a source accessor. + +## Import + ```ts import { createDeferred } from "solid-js"; +``` + +## Type + +```ts +type Accessor = () => T; function createDeferred( - source: () => T, + source: Accessor, options?: { timeoutMs?: number; equals?: false | ((prev: T, next: T) => boolean); name?: string; } -): () => T; +): Accessor; ``` -Creates a readonly that only notifies downstream changes when the browser is idle. -`timeoutMs` is the maximum time to wait before forcing the update. +## Parameters + +### `source` + +- **Type:** `Accessor` +- **Required:** Yes + +Accessor used as the deferred source. + +### `options` + +#### `timeoutMs` + +- **Type:** `number` + +Maximum delay before the deferred value is updated. + +#### `equals` + +- **Type:** `false | ((prev: T, next: T) => boolean)` + +Comparison function used to determine whether the deferred accessor should notify dependents. + +#### `name` + +- **Type:** `string` + +Debug name used by development tooling. + +## Return value + +- **Type:** `Accessor` + +Returns an accessor that exposes the deferred value. + +## Behavior + +- The deferred accessor updates after the browser becomes idle or after `timeoutMs` is reached. +- The returned accessor can lag behind the source accessor. +- `equals` controls whether downstream dependents are notified for a new value. + +## Examples + +### Basic usage + +```tsx +import { createDeferred, createSignal } from "solid-js"; + +function Example() { + const [value, setValue] = createSignal(""); + const deferredValue = createDeferred(value); + + return ( + <> + setValue(event.currentTarget.value)} + /> +
{deferredValue()}
+ + ); +} +``` -## Options +## Related -| Name | Type | Description | -| --------- | ------------------------------------------ | ------------------------------------------------------ | -| timeoutMs | `number` | The maximum time to wait before forcing the update. | -| equals | `false or ((prev: T, next: T) => boolean)` | A function that returns true if the value has changed. | -| name | `string` | The name of the readonly. | +- [`createMemo`](/reference/basic-reactivity/create-memo) +- [`startTransition`](/reference/reactive-utilities/start-transition) diff --git a/src/routes/reference/secondary-primitives/create-reaction.mdx b/src/routes/reference/secondary-primitives/create-reaction.mdx index 0ef6c6dea..9390c818e 100644 --- a/src/routes/reference/secondary-primitives/create-reaction.mdx +++ b/src/routes/reference/secondary-primitives/create-reaction.mdx @@ -42,15 +42,9 @@ Callback invoked when the tracked computation is invalidated. ### `options` -- **Type:** `{ name?: string }` -- **Required:** No - -Optional configuration object. - #### `name` - **Type:** `string` -- **Required:** No Debug name used by development tooling. diff --git a/src/routes/reference/secondary-primitives/create-selector.mdx b/src/routes/reference/secondary-primitives/create-selector.mdx index a2b4e7ece..205f0e94c 100644 --- a/src/routes/reference/secondary-primitives/create-selector.mdx +++ b/src/routes/reference/secondary-primitives/create-selector.mdx @@ -11,66 +11,92 @@ tags: - optimization version: "1.0" description: >- - Optimize selection state with createSelector. Reduce DOM updates from n to 2 - when tracking active items in lists, tabs, or dropdowns. + Create a keyed boolean accessor that reports whether a key matches the current + source value. --- +`createSelector` returns a keyed boolean accessor derived from a source accessor. + +## Import + ```ts import { createSelector } from "solid-js"; +``` + +## Type + +```ts +type Accessor = () => T; -function createSelector( - source: () => T, - fn?: (a: U, b: T) => boolean +function createSelector( + source: Accessor, + fn?: (key: U, value: T) => boolean, + options?: { name?: string } ): (key: U) => boolean; ``` -Creates a parameterized derived boolean signal `selector(key)` that indicates -whether `key` is equal to the current value of the `source` signal. -These signals are optimized to notify each subscriber only when their `key` -starts or stops matching the reactive `source` value -(instead of every time `key` changes). -If you have _n_ different subscribers with different keys, -and the `source` value changes from `a` to `b`, then -instead of all _n_ subscribers updating, -at most two subscribers will update: -the signal with key `a` will change to `false`, -and the signal with key `b` will change to `true`. -Thus it reduces from _n_ updates to 2 updates. - -Useful for defining the selection state of several selectable elements. -For example: +## Parameters -```tsx -const [selectedId, setSelectedId] = createSignal() -const isSelected = createSelector(selectedId) +### `source` - - {(item) =>
  • {item.name}
  • } -
    -``` +- **Type:** `Accessor` +- **Required:** Yes + +Accessor used as the selection source. + +### `fn` + +- **Type:** `(key: U, value: T) => boolean` -In the code above, each `li` element receives an `active` class -exactly when the corresponding `item.id` is equal to `selectedId()`. -When the `selectedId` signal changes, the `li` element(s) that previously -had previously matching `id` get the `active` class removed, and the -`li` element(s) that now have a matching `id` get the `active` class added. -All other `li` elements get skipped, so if `id`s are distinct, -only 2 DOM operations get performed. +Comparison function used to match a key against the current source value. -By contrast, the following code would perform `list().length` DOM operations -every time the `selectedId` signal changes: +### `options` + +#### `name` + +- **Type:** `string` + +Debug name used by development tooling. + +## Return value + +- **Type:** `(key: U) => boolean` + +Returns an accessor function that reports whether the provided key matches the current source value. + +## Behavior + +- The returned function compares each key against the current source value. +- With the default comparison, matching uses strict equality. +- Subscribers update when their key starts or stops matching the source value. + +## Examples + +### Basic usage ```tsx -const [selectedId, setSelectedId] = createSignal() +import { createSelector, createSignal, For } from "solid-js"; + +function Example(props) { + const [selectedId, setSelectedId] = createSignal(); + const isSelected = createSelector(selectedId); - - {(item) =>
  • {item.name}
  • } -
    + return ( + + {(item) => ( +
  • setSelectedId(item.id)} + > + {item.name} +
  • + )} +
    + ); +} ``` -## Arguments +## Related -| Name | Type | Description | -| :------- | :------------------------ | :------------------------------------------------------------------------------------------------------------- | -| `source` | `() => T` | The source signal to get the value from and compare with keys. | -| `fn` | `(a: U, b: T) => boolean` | A function to compare the key and the value, returning whether they should be treated as equal. Default: `===` | +- [`createMemo`](/reference/basic-reactivity/create-memo) +- [``](/reference/components/for) From 2057f6abb9b258a03a6dcfbf57414027f758fcfb Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 13:25:05 -0700 Subject: [PATCH 07/88] docs: update documentation for , mergeProps, , and splitProps --- src/routes/reference/components/dynamic.mdx | 108 ++++++++++++++---- src/routes/reference/components/portal.mdx | 95 +++++++++++---- .../reactive-utilities/merge-props.mdx | 68 ++++++++--- .../reactive-utilities/split-props.mdx | 75 ++++++++---- 4 files changed, 265 insertions(+), 81 deletions(-) diff --git a/src/routes/reference/components/dynamic.mdx b/src/routes/reference/components/dynamic.mdx index ab3c721d6..8229c7e26 100644 --- a/src/routes/reference/components/dynamic.mdx +++ b/src/routes/reference/components/dynamic.mdx @@ -2,45 +2,109 @@ title: order: 5 use_cases: >- - dynamic components, conditional rendering, polymorphic components, runtime - component selection, flexible ui, component switching + dynamic components, runtime component selection, polymorphic components, + rendering custom components or intrinsic elements tags: - dynamic - components - jsx - polymorphic - rendering - - conditional version: "1.0" description: >- - Render components dynamically at runtime with the Dynamic component. Perfect - for polymorphic components and conditional component rendering. + Render a component or intrinsic element selected at runtime, and access the + lower-level `createDynamic` helper. --- -This component lets you insert an arbitrary Component or tag and passes the props through to it. +`` renders the value of its `component` prop as either a custom component or an intrinsic element. `createDynamic` provides the same behavior as a function. + +## Import + +```ts +import { Dynamic, createDynamic } from "solid-js/web"; +``` + +## Type + +```ts +type ValidComponent = + | keyof JSX.IntrinsicElements + | ((props: any) => JSX.Element); + +type DynamicProps = { + component: T | undefined; +} & Record; + +function createDynamic( + component: () => T | undefined, + props: Record +): JSX.Element; + +function Dynamic(props: DynamicProps): JSX.Element; +``` + +## Props + +### `component` + +- **Type:** `T | undefined` + +Component or intrinsic element to render. + +### remaining props + +- **Type:** props accepted by the rendered component or element + +Props forwarded to the rendered value of `component`. + +## Parameters + +### `component` + +- **Type:** `() => T | undefined` + +Accessor that returns the component or intrinsic element to render. + +### `props` + +- **Type:** `Record` + +Props passed to the rendered component or element. + +## Return value + +- **Type:** `JSX.Element` + +Returns the rendered component or element. + +## Behavior + +- When `component` resolves to a function, Solid calls that component with the provided props. +- When `component` resolves to a string, Solid creates an intrinsic element of that type and spreads the provided props onto it. +- `` calls `createDynamic(() => props.component, others)` internally after separating the `component` prop from the remaining props. + +## Examples + +### Basic usage ```tsx import { Dynamic } from "solid-js/web"; -import type { JSX } from "solid-js"; - -function Dynamic( - props: T & { - children?: any; - component?: Component | string | keyof JSX.IntrinsicElements; - } -): () => JSX.Element; + +function Example(props) { + return ; +} ``` -Here's an example of how you can use it: +### `createDynamic` ```tsx - +import { createDynamic } from "solid-js/web"; + +function Example(props) { + return createDynamic(() => props.as, { value: props.value }); +} ``` -## Props +## Related -| Name | Type | Description | -| :---------- | :---------------------------------------------------------- | :---------------------------------------- | -| `component` | `Component` \| `string` \| `keyof JSX.IntrinsicElements` | The component to render. | -| `children` | `any` | The children to pass to the component. | -| `...` | `T` | Any other props to pass to the component. | +- [``](/reference/components/portal) diff --git a/src/routes/reference/components/portal.mdx b/src/routes/reference/components/portal.mdx index 1dffac6a5..23c9b7449 100644 --- a/src/routes/reference/components/portal.mdx +++ b/src/routes/reference/components/portal.mdx @@ -1,52 +1,103 @@ --- title: -use_cases: "modals, tooltips, overlays, popups, dropdowns, ui layers, escape dom hierarchy" +use_cases: >- + modals, tooltips, overlays, popups, dropdowns, rendering outside parent DOM + hierarchy tags: - portal - - modals - dom - overlays - tooltips - components version: "1.0" description: >- - Render SolidJS components outside parent DOM hierarchy with Portal. Perfect - for modals, tooltips, and overlays that need to escape layout. + Render children into a DOM node outside the parent DOM hierarchy. --- -`` is a component that allows you to render children into a DOM node that exists outside the DOM hierarchy of the parent component. +`` renders its children into a different DOM node while preserving the Solid component hierarchy. -This is useful when your UI has some elements that need to appear on top of everything else, such as modals and tooltips. +## Import -```tsx +```ts import { Portal } from "solid-js/web"; -import type { JSX } from "solid-js"; +``` +## Type + +```ts function Portal(props: { mount?: Node; useShadow?: boolean; isSVG?: boolean; + ref?: + | HTMLDivElement + | SVGGElement + | ((el: HTMLDivElement | SVGGElement) => void); children: JSX.Element; }): Text; ``` -This inserts the element in the mount node. -Useful for inserting Modals outside of the page layout. -Events still propagate through the component hierarchy, however `` will only run on the client and has hydration _disabled_. +## Props + +### `mount` + +- **Type:** `Node` + +Mount point for the portal. When omitted, the portal mounts to `document.body`. + +### `useShadow` + +- **Type:** `boolean` + +Creates a shadow root on the portal container when supported. + +### `isSVG` + +- **Type:** `boolean` + +Creates an SVG `` container instead of an HTML `
    `. + +### `ref` -The portal is mounted in a `
    ` unless the target is the document head. -`useShadow` places the element in a Shadow Root for style isolation, and `isSVG` is required if inserting into an SVG element so that the `
    ` is not inserted. +- **Type:** `HTMLDivElement | SVGGElement | ((el: HTMLDivElement | SVGGElement) => void)` + +Receives the portal container element. + +### `children` + +- **Type:** `JSX.Element` + +Content rendered into the portal container. + +## Return value + +- **Type:** `Text` + +Returns a marker node used to preserve the portal position in the component tree. + +## Behavior + +- `` renders its children outside the parent DOM hierarchy, but events still propagate through the Solid component hierarchy. +- When the mount target is not `document.head`, the portal creates a container element and appends it to the mount target. +- When the mount target is `document.head`, the portal inserts its children without creating that container element. +- Portals render only on the client and are skipped during hydration. + +## Examples + +### Basic usage ```tsx - -
    My Content
    -
    +import { Portal } from "solid-js/web"; + +function Example() { + return ( + +
    My Content
    +
    + ); +} ``` -## Props +## Related -| Name | Type | Default | Description | -| :---------- | :-------- | :------------ | :------------------------------------------------ | -| `mount` | `Node` | document.body | The DOM node to mount the portal in. | -| `useShadow` | `boolean` | false | Whether to use a Shadow Root for style isolation. | -| `isSVG` | `boolean` | false | Whether the mount node is an SVG element. | +- [``](/reference/components/dynamic) diff --git a/src/routes/reference/reactive-utilities/merge-props.mdx b/src/routes/reference/reactive-utilities/merge-props.mdx index 85eab1176..b804b7d7d 100644 --- a/src/routes/reference/reactive-utilities/merge-props.mdx +++ b/src/routes/reference/reactive-utilities/merge-props.mdx @@ -1,41 +1,75 @@ --- title: mergeProps use_cases: >- - component props, default values, prop cloning, prop merging, component - configuration + component props, default values, prop cloning, combining multiple prop + sources tags: - props - components - reactivity - defaults - - cloning - merging version: "1.0" description: >- - Learn how to merge reactive props in SolidJS for setting component defaults, - cloning props objects, and combining multiple prop sources dynamically. + Merge multiple prop sources into a single object while preserving reactive + property access. --- +`mergeProps` merges prop sources from left to right and resolves reads from the last source that defines each property. + +## Import + ```ts import { mergeProps } from "solid-js"; +``` -function mergeProps(...sources: any): any; +## Type + +```ts +function mergeProps( + ...sources: T +): Record; ``` -A reactive object **merge** method. -Useful for setting default props for components in case caller doesn't provide them. -Or cloning the props object including reactive properties. +## Parameters + +### `sources` + +- **Type:** `unknown[]` + +Prop sources to merge. + +## Return value + +- **Type:** `Record` + +Returns an object that resolves each property from the last source that defines it. -This method works by using a proxy and resolving properties in reverse order. -This allows for dynamic tracking of properties that aren't present when the prop object is first merged. +## Behavior + +- Later sources override earlier sources for the same property. +- Function sources are wrapped so property reads stay reactive. +- When reactive proxies are involved, the merged result uses proxy-backed property resolution. +- Property lookups are resolved when read rather than copied eagerly from every source. + +## Examples + +### Basic usage ```ts -// default props -props = mergeProps({ name: "Smith" }, props); +import { mergeProps } from "solid-js"; + +const props = mergeProps({ name: "Smith" }, incomingProps); +``` -// clone props -newProps = mergeProps(props); +### Clone props -// merge props -props = mergeProps(props, otherProps); +```ts +import { mergeProps } from "solid-js"; + +const clonedProps = mergeProps(props); ``` + +## Related + +- [`splitProps`](/reference/reactive-utilities/split-props) diff --git a/src/routes/reference/reactive-utilities/split-props.mdx b/src/routes/reference/reactive-utilities/split-props.mdx index 0973baf63..1f7e03fe9 100644 --- a/src/routes/reference/reactive-utilities/split-props.mdx +++ b/src/routes/reference/reactive-utilities/split-props.mdx @@ -1,8 +1,8 @@ --- title: splitProps use_cases: >- - prop forwarding, component composition, prop separation, child components, - prop destructuring + prop forwarding, component composition, prop separation, splitting props into + multiple groups tags: - props - components @@ -11,27 +11,61 @@ tags: - reactive version: "1.0" description: >- - Split reactive props objects by keys in SolidJS. Perfect for consuming - specific props while forwarding others to child components efficiently. + Split a reactive props object into multiple reactive subsets and a remainder + object. --- +`splitProps` partitions a props object by key groups and returns a reactive object for each group plus a final object containing the remaining keys. + +## Import + ```ts import { splitProps } from "solid-js"; +``` + +## Type -function splitProps( +```ts +function splitProps>( props: T, - ...keys: Array<(keyof T)[]> -): [...parts: Partial]; + ...keys: (readonly (keyof T)[])[] +): Record[]; ``` -Splits a reactive object by keys. +## Parameters + +### `props` + +- **Type:** `T` + +Source props object. + +### `keys` + +- **Type:** `(readonly (keyof T)[])[]` + +Arrays of keys that determine each returned subset. + +## Return value + +- **Type:** `Record[]` -It takes a reactive object and any number of arrays of keys; for each array of keys, it will return a reactive object with just those properties of the original object. -The last reactive object in the returned array will have any leftover properties of the original object. +Returns one reactive object for each key array and a final object containing keys not assigned to any earlier group. -This can be useful if you want to consume a subset of props and pass the rest to a child. +## Behavior + +- Each returned object preserves reactive property access. +- Keys are assigned to the first matching group. +- The last returned object contains keys not included in the provided key arrays. +- When the source props object is proxy-backed, the returned objects use proxy-backed property access. + +## Examples + +### Basic usage ```tsx +import { splitProps } from "solid-js"; + function MyComponent(props) { const [local, others] = splitProps(props, ["children"]); @@ -44,23 +78,24 @@ function MyComponent(props) { } ``` -Because `splitProps` takes any number of arrays, we can split a props object as much as we wish (if, for example, we had multiple child components that each required a subset of the props). - -Let's say a component was passed six props: +### Split multiple groups ```tsx -; -// ... +import { splitProps } from "solid-js"; function MyComponent(props) { - console.log(props); // {a: 1, b: 2, c: 3, d: 4, e: 5, foo: "bar"} const [vowels, consonants, leftovers] = splitProps( props, ["a", "e"], ["b", "c", "d"] ); - console.log(vowels); // {a: 1, e: 5} - console.log(consonants); // {b: 2, c: 3, d: 4} - console.log(leftovers.foo); // bar + + return ( + + ); } ``` + +## Related + +- [`mergeProps`](/reference/reactive-utilities/merge-props) From 0374d22c2cfbeb24f76cc3e9717ebf67a2a4c134 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 13:36:17 -0700 Subject: [PATCH 08/88] docs: update documentation for , mergeProps, , and splitProps --- src/routes/reference/components/for.mdx | 89 +++++++--- .../reference/components/index-component.mdx | 95 +++++++---- src/routes/reference/components/show.mdx | 156 ++++++++++-------- .../reference/components/switch-and-match.mdx | 111 +++++++++---- 4 files changed, 283 insertions(+), 168 deletions(-) diff --git a/src/routes/reference/components/for.mdx b/src/routes/reference/components/for.mdx index 2128f18a8..631b9a82d 100644 --- a/src/routes/reference/components/for.mdx +++ b/src/routes/reference/components/for.mdx @@ -13,53 +13,88 @@ tags: - performance version: "1.0" description: >- - Efficiently render lists in SolidJS with the For component. Provides keyed - iteration with minimal DOM updates for dynamic array rendering. + Render a list by item identity with a child function that receives the item + and an index accessor. --- -The `` component is used to render a list of items. It is similar to the `.map()` function in JavaScript. +`` renders a list by item identity. + +## Import ```ts import { For } from "solid-js"; -import type { JSX } from "solid-js"; +``` -function For(props: { - each: readonly T[]; +## Type + +```ts +type Accessor = () => T; + +function For(props: { + each: T | undefined | null | false; fallback?: JSX.Element; - children: (item: T, index: () => number) => U; -}): () => U[]; + children: (item: T[number], index: Accessor) => U; +}): JSX.Element; ``` -A referentially keyed loop with efficient updating of only changed items. The callback takes the current item as the first argument: +## Props -```jsx -Loading...
    }> - {(item) =>
    {item}
    } - -``` +### `each` + +- **Type:** `T | undefined | null | false` + +Source list. + +### `fallback` + +- **Type:** `JSX.Element` + +Content rendered when `each` is empty or falsy. + +### `children` -The `each` prop can also be a function that returns a list. This is useful for creating a loop that depends on a state value: +- **Type:** `(item: T[number], index: Accessor) => U` -```jsx -{(item) =>
    {item}
    }
    +Child function. It receives the current item and an index accessor. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- `` maps items by value identity. +- The `index` argument is an accessor. +- Reordering reuses rendered items whose source values are retained. +- `` uses [`mapArray`](/reference/reactive-utilities/map-array) internally. + +## Examples + +### Basic usage + +```tsx +import { For } from "solid-js"; + +No items
    }> + {(item) =>
    {item}
    } +; ``` -The optional second argument is an index signal: +### Access the index -```jsx -Loading...
    }> +```tsx +import { For } from "solid-js"; + + {(item, index) => (
    #{index()} {item}
    )} -
    +
    ; ``` -## Props +## Related -| Name | Type | Description | -| :--------- | :------------------------------------ | :--------------------------------------------------------------- | -| `each` | `readonly T[]` | The list of items to render. | -| `fallback` | `JSX.Element` | A fallback element to render while the list is loading. | -| `children` | `(item: T, index: () => number) => U` | A callback that returns a JSX element for each item in the list. | +- [``](/reference/components/index-component) +- [`mapArray`](/reference/reactive-utilities/map-array) diff --git a/src/routes/reference/components/index-component.mdx b/src/routes/reference/components/index-component.mdx index 3c42430c3..4ec170c1c 100644 --- a/src/routes/reference/components/index-component.mdx +++ b/src/routes/reference/components/index-component.mdx @@ -12,64 +12,89 @@ tags: - index version: "1.0" description: >- - Render non-keyed lists in SolidJS with Index component. Perfect for primitive - arrays where index position matters more than item identity. + Render a list by index with a child function that receives an item accessor + and a numeric index. --- -Non-keyed list iteration (rendered nodes are keyed to an array index). This is useful when there is no conceptual key, like if the data consists of primitives and it is the index that is fixed rather than the value. +`` renders a list by index. + +## Import ```ts import { Index } from "solid-js"; -import type { JSX } from "solid-js"; - -function Index(props: { - each: readonly T[]; - fallback?: JSX.Element; - children: (item: () => T, index: number) => U; -}): () => U[]; ``` -A super simple implementation of this component might look like this: +## Type -```tsx -function Index(props: { - each: readonly T[]; +```ts +type Accessor = () => T; + +function Index(props: { + each: T | undefined | null | false; fallback?: JSX.Element; - children: (item: () => T, index: number) => U; -}) { - return () => { - const [items, setItems] = createSignal(props.each); - return props.each.map((_, i) => props.children(() => items()[i], i)); - }; -} + children: (item: Accessor, index: number) => U; +}): JSX.Element; ``` -Here's a look at the implementation of the `Index` component in Solid: +## Props + +### `each` + +- **Type:** `T | undefined | null | false` + +Source list. + +### `fallback` + +- **Type:** `JSX.Element` + +Content rendered when `each` is empty or falsy. + +### `children` + +- **Type:** `(item: Accessor, index: number) => U` + +Child function. It receives an accessor for the item at that index and the index number. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- `` maps items by index rather than by value identity. +- The `item` argument is an accessor. +- The `index` argument is a number. +- Updating a value at the same index updates the corresponding rendered item. +- `` uses [`indexArray`](/reference/reactive-utilities/index-array) internally. + +## Examples + +### Basic usage ```tsx -Loading...}> +import { Index } from "solid-js"; + +No items}> {(item) =>
    {item()}
    } -
    +
    ; ``` -Notice that the item is a signal unlike the `For` component. This is because the `Index` component is not keyed to the item itself but rather the index. - -Optional second argument is an index number: +### Access the index ```tsx -Loading...}> +import { Index } from "solid-js"; + + {(item, index) => (
    #{index} {item()}
    )} -
    +
    ; ``` -## Props +## Related -| Name | Type | Description | -| :------- | :------------------------------------ | :-------------------------------------------------------------- | -| each | `readonly T[]` | The array to iterate over. | -| fallback | `JSX.Element` | Optional fallback element to render while the array is loading. | -| children | `(item: () => T, index: number) => U` | The function that renders the children. | +- [``](/reference/components/for) +- [`indexArray`](/reference/reactive-utilities/index-array) diff --git a/src/routes/reference/components/show.mdx b/src/routes/reference/components/show.mdx index 7f51f9ca6..b5d843956 100644 --- a/src/routes/reference/components/show.mdx +++ b/src/routes/reference/components/show.mdx @@ -13,92 +13,106 @@ tags: - toggle version: "1.0" description: >- - Conditionally render UI elements in SolidJS with Show component. Display - content based on truthy conditions with optional fallback support. + Render children when a condition is truthy, with optional fallback content + and keyed behavior. --- -The `` component is used for conditionally rendering UI elements. -It takes a `when` prop that defines the condition for rendering. -When the `when` prop is truthy, the children of the `` component are displayed. -Additionally, an optional `fallback` prop can be provided to specify an element that is shown when the condition is falsy. +`` renders its children when `when` is truthy and renders `fallback` otherwise. -```tsx -import { createSignal, Show } from "solid-js"; - -function Example() { - const [value, setValue] = createSignal(true); - return ( -
    - - Fallback Element
    }> -
    Child Element
    -
    - - ); -} +## Import + +```ts +import { Show } from "solid-js"; +``` + +## Type + +```ts +type Accessor = () => T; + +function Show(props: { + when: T | undefined | null | false; + keyed?: false; + fallback?: JSX.Element; + children: JSX.Element | ((item: Accessor>) => JSX.Element); +}): JSX.Element; + +function Show(props: { + when: T | undefined | null | false; + keyed: true; + fallback?: JSX.Element; + children: JSX.Element | ((item: NonNullable) => JSX.Element); +}): JSX.Element; ``` -## Keyed rendering +## Props + +### `when` + +- **Type:** `T | undefined | null | false` + +Condition value that determines whether the children are rendered. + +### `keyed` + +- **Type:** `boolean` + +Controls whether function children receive the current value directly instead of an accessor. + +### `fallback` -When the `keyed` is set to `true`, any change to the `when` prop — including changes in its reference — will cause the `` component to re-render its children. +- **Type:** `JSX.Element` + +Content rendered when `when` is falsy. + +### `children` + +- **Type:** `JSX.Element | ((item) => JSX.Element)` + +Content rendered when `when` is truthy. Function children receive an accessor or value based on `keyed`. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- With `keyed` omitted or `false`, `` re-renders only when the truthiness of `when` changes. +- With `keyed={true}`, changes to the `when` value itself trigger a new render even when the value remains truthy. +- Function children are wrapped in [`untrack`](/reference/reactive-utilities/untrack). +- With `keyed={false}`, function children receive an accessor for the current non-nullish value; with `keyed={true}`, they receive the value directly. + +## Examples + +### Basic usage ```tsx -import { createSignal, Show } from "solid-js"; - -function KeyedExample() { - const [user, setUser] = createSignal({ name: "John Wick" }); - - function update() { - // This operation changes the reference of the user object. - setUser({ ...user() }); - } - - return ( -
    - - -

    Name: {user().name}

    - {/* Updates shown with each click */} -

    Last updated: {Date.now()}

    -
    -
    - ); -} +import { Show } from "solid-js"; + +Fallback Element}> +
    Child Element
    +
    ; ``` -## Render function +### Function child -The `` component can also accept a render function as its child. -In this case, the first argument of the render function is an _accessor_ to the `when` prop. -However, when the `keyed` prop is set to `true`, the argument is the `when` prop itself instead of an accessor. +```tsx +import { Show } from "solid-js"; -When a render function is used, it is internally wrapped with [`untrack`](/reference/reactive-utilities/untrack). -As a result, signals accessed directly within the render function's scope will not react to updates. +{(user) =>
    {user().name}
    }
    ; +``` -For example, in the following code, the count displayed in the first `` component does not update when the `count` signal changes. -However, the second `` component does update since the `count` signal is accessed within a JSX element, which creates a tracking scope. +### Keyed function child ```tsx -import { createSignal, Show } from "solid-js"; - -function RenderFunctionExample() { - const [count, setCount] = createSignal(0); - return ( -
    - - {/* This does not update. */} - {(c) => count()} - {/* This will update. */} - {(c) => <>{count()}} -
    - ); -} +import { Show } from "solid-js"; + + + {(user) =>
    {user.name}
    } +
    ; ``` -## Props +## Related -| Name | Type | Description | -| :--------- | :-------------------------------- | :---------------------------------------------------- | -| `when` | `T \| undefined \| null \| false` | The condition value. | -| `keyed` | `boolean` | Whether to key the block to the value of when. | -| `fallback` | `JSX.Element` | The fallback to render when the `when` prop is falsy. | +- [`untrack`](/reference/reactive-utilities/untrack) +- [` / `](/reference/components/switch-and-match) diff --git a/src/routes/reference/components/switch-and-match.mdx b/src/routes/reference/components/switch-and-match.mdx index 98d1dcbfa..e4e65de38 100644 --- a/src/routes/reference/components/switch-and-match.mdx +++ b/src/routes/reference/components/switch-and-match.mdx @@ -13,48 +13,91 @@ tags: - switch version: "1.0" description: >- - Handle multiple exclusive conditions in SolidJS with Switch and Match. Clean - alternative to if-else chains for complex conditional rendering. + Render the first matching branch from a set of mutually exclusive conditions. --- -Useful for when there are more than 2 mutual exclusive conditions. It is a more flexible version of the if-else-if-else-if-else-... chain. +`` renders the first child `` whose `when` prop is truthy. `` can render JSX directly or accept a function child. + +## Import + +```ts +import { Match, Switch } from "solid-js"; +``` + +## Type ```ts -import { Switch, Match } from "solid-js"; -import type { MatchProps, JSX } from "solid-js"; +type Accessor = () => T; function Switch(props: { fallback?: JSX.Element; children: JSX.Element; -}): () => JSX.Element; +}): JSX.Element; + +function Match(props: { + when: T | undefined | null | false; + keyed?: false; + children: JSX.Element | ((item: Accessor>) => JSX.Element); +}): JSX.Element; -type MatchProps = { +function Match(props: { when: T | undefined | null | false; - children: JSX.Element | ((item: T) => JSX.Element); -}; -function Match(props: MatchProps); + keyed: true; + children: JSX.Element | ((item: NonNullable) => JSX.Element); +}): JSX.Element; ``` -A super simple implementation of this component would be: +## `` props -```tsx -function Switch(props) { - let children = props.children; +### `fallback` - if (!Array.isArray(children)) children = [children]; +- **Type:** `JSX.Element` - for (let i = 0; i < children.length; i++) { - const child = children[i]; - if (child.props.when) return child; - } +Content rendered when no child `` has a truthy `when` value. - return props.fallback; -} -``` +### `children` + +- **Type:** `JSX.Element` + +Child `` elements. + +## `` props + +### `when` + +- **Type:** `T | undefined | null | false` + +Condition value for the branch. -For example, it can be used to perform basic routing: +### `keyed` + +- **Type:** `boolean` + +Controls whether function children receive the current value directly instead of an accessor. + +### `children` + +- **Type:** `JSX.Element | ((item) => JSX.Element)` + +Content rendered when the branch matches. Function children receive an accessor or value based on `keyed`. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- `` evaluates its child `` elements in order and renders the first truthy branch. +- If no branch matches, `` renders `fallback`. +- Function children in `` follow the same keyed behavior as [``](/reference/components/show): with `keyed={false}`, they receive an accessor; with `keyed={true}`, they receive the value directly. + +## Examples + +### Basic usage ```tsx +import { Match, Switch } from "solid-js"; + Not Found}> @@ -62,21 +105,19 @@ For example, it can be used to perform basic routing: - +; ``` -Match also supports function children to serve as keyed flow. +### Function child -## Props - -### Switch +```tsx +import { Match, Switch } from "solid-js"; -| Name | Type | Default | Description | -| ---------- | ------------- | ----------- | -------------------------------------------------------------------------------- | -| `fallback` | `JSX.Element` | `undefined` | The fallback element to render if no `Match` component has a truthy `when` prop. | + + {(user) =>
    {user().name}
    }
    +
    ; +``` -### Match +## Related -| Name | Type | Default | Description | -| ------ | --------------------------------- | ----------- | ------------------------------------------------------------------------- | -| `when` | `T \| undefined \| null \| false` | `undefined` | The condition to check. If it is truthy, the `children` will be rendered. | +- [``](/reference/components/show) From b1c0bd4dc4020c63a6b7d6e1fd3c27d66b4b82b8 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 13:53:36 -0700 Subject: [PATCH 09/88] docs: update documentation for , mergeProps, , and splitProps --- .../reference/components/error-boundary.mdx | 91 +++++----- .../reference/components/no-hydration.mdx | 58 ++++-- .../reference/components/suspense-list.mdx | 86 +++++---- src/routes/reference/components/suspense.mdx | 165 +++++------------- 4 files changed, 198 insertions(+), 202 deletions(-) diff --git a/src/routes/reference/components/error-boundary.mdx b/src/routes/reference/components/error-boundary.mdx index e3c295ae5..c222b6032 100644 --- a/src/routes/reference/components/error-boundary.mdx +++ b/src/routes/reference/components/error-boundary.mdx @@ -2,67 +2,80 @@ title: order: 5 use_cases: >- - error handling, production apps, preventing crashes, user experience, - debugging, fallback ui + error handling, rendering fallbacks, preventing subtree crashes, recovering + from render errors tags: - error-handling - components - - debugging - fallback - - production - stability version: "1.0" description: >- - Catch and handle rendering errors in SolidJS components with ErrorBoundary. - Display fallback UI when errors occur in children components. + Catch errors thrown while rendering or updating a subtree and render fallback + content. --- -The `` component catches errors that occur during the rendering or updating of its children and shows a fallback UI instead. -This includes: +`` catches errors thrown while rendering or updating its children. -- Errors that occur while rendering JSX. -- Errors that occur within `createEffect`, `createMemo`, and other state management primitives. -- Errors that occur within `createResource` and other asynchronous state management or data-fetching primitives. +## Import -However, errors occurring outside the rendering process are **not** captured by error boundaries. -For instance: +```ts +import { ErrorBoundary } from "solid-js"; +``` -- Errors that occur inside event handlers. -- Errors that occur after a `setTimeout`. +## Type + +```ts +function ErrorBoundary(props: { + fallback: JSX.Element | ((err: any, reset: () => void) => JSX.Element); + children: JSX.Element; +}): JSX.Element; +``` ## Props ### `fallback` -**Type**: `JSX.Element | ((err: any, reset: () => void) => JSX.Element)` +- **Type:** `JSX.Element | ((err: any, reset: () => void) => JSX.Element)` + +Fallback content. Function fallbacks receive the caught error and a reset function. + +### `children` + +- **Type:** `JSX.Element` -`fallback` provides content to display when an error occurs. -If a function is passed, it receives two parameters: +Subtree wrapped by the boundary. -- `err`: The caught error object. -- `reset`: A function that forces the `` to re-render its children and clear the error state. +## Return value -If there's an error within the `fallback` itself, however, it is not caught by the same ``. -Instead, it will bubble up to any parent error boundaries. +- **Type:** `JSX.Element` -## Example +## Behavior + +- `` catches errors thrown while rendering JSX and while updating reactive computations in its subtree. +- Errors thrown from event handlers or from callbacks scheduled outside Solid's rendering and update flow are not caught by the boundary. +- Function fallbacks receive `reset`, which clears the current error state and re-renders the children. +- Errors thrown by the fallback itself can be caught by a parent error boundary. + +## Examples + +### Basic usage ```tsx import { ErrorBoundary } from "solid-js"; -import { ErrorProne } from "./components"; - -function Example() { - return ( - ( -
    -

    {error.message}

    - -
    - )} - > - -
    - ); -} + + ( +
    +

    {error.message}

    + +
    + )} +> + +
    ; ``` + +## Related + +- [`catchError`](/reference/reactive-utilities/catch-error) diff --git a/src/routes/reference/components/no-hydration.mdx b/src/routes/reference/components/no-hydration.mdx index eb142215f..0064ddb96 100644 --- a/src/routes/reference/components/no-hydration.mdx +++ b/src/routes/reference/components/no-hydration.mdx @@ -1,35 +1,57 @@ --- title: use_cases: >- - static content, ssr optimization, performance, reducing bundle size, - server-only rendering + static content, server-rendered content, skipping hydration for a subtree tags: - ssr - hydration - - optimization - - performance - - static - server-rendering + - components version: "1.0" description: >- - Prevent client-side hydration for static content in SolidJS. Optimize - performance by skipping hydration for server-rendered static elements. + Render children during server rendering and skip hydration for that subtree + on the client. --- -The `` component prevents the client-side hydration process from being applied to its children. -During server-side rendering, components and elements wrapped within `` will render normally on the server, contributing to the initial HTML output. -However, during client-side hydration, Solid bypasses the hydration process for the content within ``. -This means that elements within `` will not have event listeners attached by Solid, and their state will not be managed reactively on the client-side after the initial render. +`` skips hydration for its subtree on the client. -:::note -Placing a `` component inside `` has no effect and will not override the `` behavior. -::: +## Import -## Example +```ts +import { NoHydration } from "solid-js/web"; +``` + +## Type + +```ts +function NoHydration(props: { children?: JSX.Element }): JSX.Element; +``` + +## Props + +### `children` + +- **Type:** `JSX.Element` + +Content inside the boundary. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- During server rendering, children inside `` render normally. +- During client hydration, Solid skips hydrating that subtree. +- Event listeners and reactive updates are not attached to content inside the boundary during hydration. +- Placing `` inside `` does not override the no-hydration behavior. + +## Examples + +### Basic usage ```tsx import { NoHydration } from "solid-js/web"; -import { InteractiveComponent, StaticContent } from "./components"; function Example() { return ( @@ -42,3 +64,7 @@ function Example() { ); } ``` + +## Related + +- [``](/reference/components/portal) diff --git a/src/routes/reference/components/suspense-list.mdx b/src/routes/reference/components/suspense-list.mdx index fa7754279..a819185c9 100644 --- a/src/routes/reference/components/suspense-list.mdx +++ b/src/routes/reference/components/suspense-list.mdx @@ -2,8 +2,8 @@ title: order: 5 use_cases: >- - coordinating loading, multiple async, loading sequences, layout stability, - parallel data fetching + coordinating suspense boundaries, controlling reveal order, coordinating + fallback display tags: - suspense - async @@ -13,16 +13,20 @@ tags: - components version: "1.0" description: >- - Coordinate multiple Suspense components in SolidJS. Control reveal order and - manage loading states for parallel async operations smoothly. + Coordinate how multiple suspense boundaries reveal their content. --- -SuspenseList allows for coordinating multiple parallel Suspense and SuspenseList components. It controls the order in which content is revealed to reduce layout thrashing and has an option to collapse or hide fallback states. +`` coordinates nested suspense boundaries. + +## Import ```ts import { SuspenseList } from "solid-js"; -import type { JSX } from "solid-js"; +``` + +## Type +```ts function SuspenseList(props: { children: JSX.Element; revealOrder: "forwards" | "backwards" | "together"; @@ -30,37 +34,59 @@ function SuspenseList(props: { }): JSX.Element; ``` -**Note**: SuspenseList is still in the experimental stage and does not have full `SSR` support. +## Props -Here's an example of how to use SuspenseList: +### `children` -```tsx - - - Loading posts...}> - - - Loading fun facts...}> - - - -``` - -## Props +- **Type:** `JSX.Element` -| Name | Type | Default | Description | -| ------------- | ----------------------------------------- | ------------ | --------------------------------------------------------------------------- | -| `revealOrder` | `"forwards" \| "backwards" \| "together"` | `"forwards"` | Determines the order in which the SuspenseList children should be revealed. | -| `tail` | `"collapsed" \| "hidden"` | `undefined` | TODO | +Suspense and suspense-list children inside the list. ### `revealOrder` -`"forwards" | "backwards" | "together"` +- **Type:** `"forwards" | "backwards" | "together"` -- `"forwards"`: Reveals each item in the list once the previous item has finished rendering. This is the default. -- `"backwards"`: Reveals each item in the list once the next item has finished rendering. -- `"together"`: Reveals all items in the list at the same time. +Order used to reveal child boundaries. ### `tail` -`"collapsed" | "hidden"` +- **Type:** `"collapsed" | "hidden"` + +Controls fallback visibility for later items after the first still-pending item. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- `revealOrder="forwards"` reveals items from first to last. +- `revealOrder="backwards"` reveals items from last to first. +- `revealOrder="together"` reveals all items only after all coordinated boundaries are ready. +- With `tail="collapsed"`, later pending items can keep a fallback visible after the first pending item. +- With `tail="hidden"`, later pending items hide their fallback after the first pending item. + +## Notes + +- `` is experimental. + +## Examples + +### Basic usage + +```tsx +import { Suspense, SuspenseList } from "solid-js"; + + + Loading profile...}> + + + Loading posts...}> + + +; +``` + +## Related + +- [``](/reference/components/suspense) diff --git a/src/routes/reference/components/suspense.mdx b/src/routes/reference/components/suspense.mdx index 18bb5ef1b..c66a91041 100644 --- a/src/routes/reference/components/suspense.mdx +++ b/src/routes/reference/components/suspense.mdx @@ -2,157 +2,88 @@ title: order: 5 use_cases: >- - async data, loading states, data fetching, api calls, lazy loading, user - experience + async data, loading states, resources, lazy-loaded components, asynchronous + subtrees tags: - suspense - async - loading - resources - components - - data-fetching version: "1.0" description: >- - Handle async operations elegantly in SolidJS with Suspense. Show loading - states while resources load without blocking UI rendering. + Render fallback content while resources read under the boundary are pending. --- -A component that tracks all resources read under it and shows a fallback placeholder state until they are resolved. What makes `Suspense` different than `Show` is that it is non-blocking in the sense that both branches exist at the same time even if not currently in the DOM. This means that the fallback can be rendered while the children are loading. This is useful for loading states and other asynchronous operations. +`` tracks resources read under its boundary. -```tsx +## Import + +```ts import { Suspense } from "solid-js"; -import type { JSX } from "solid-js"; +``` + +## Type +```ts function Suspense(props: { fallback?: JSX.Element; children: JSX.Element; }): JSX.Element; ``` -Here's an example of a `Suspense` component that shows a loading spinner while the `User` component is loading. +## Props -```tsx -}> - - -``` +### `fallback` -## Nested Suspense - -`` is triggered whenever a resource is read under the suspense boundary, and waits until all resources read -under the suspense boundary have resolved. Often, however, you may not want this behavior. For example, if your entire page is -wrapped in suspense, you may not want a resource that only populates a certain part of the page to trigger suspense. -In that case, you can wrap that resource usage in its own suspense boundary, and the resource will only trigger the -closest suspense boundary. - -For example, in the code below, only the `title()` resource will trigger the top level suspense boundary, and only the `data()` -resource will trigger the nested suspense boundary: - -```jsx -const MyComponent = () => { - const [title] = createResource(async () => { /* fetcher code here */ }) - const [data] = createResource(async () => { /* fetcher code here */ }) - -

    {title()}

    - - {data()} - -
    -} +- **Type:** `JSX.Element` -``` +Content rendered while tracked resources are pending. -## The purpose of `` +### `children` -To understand the purpose of suspense, let's consider the following code snippets. These snippets will have some drawbacks which we will solve by using suspense. We will also see how it is possible to use `Suspense` yet not reap its benefits. +- **Type:** `JSX.Element` -Our example use case is to display a user profile. A naive snippet would look like this: +Subtree inside the suspense boundary. -```jsx -const MyComponentWithOptionalChaining = () => { - const [profile] = createResource(async () => { - /* fetcher code here */ - }); - return ( - <> -
    {profile()?.name}
    -
    {profile()?.email}
    - - ); -}; -``` +## Return value -In this code, `profile()` starts as `undefined`, and when the fetcher code finishes, resolves to an object with `name` and `email` properties. Although the resource has not resolved yet, the two `div`s are already created and attached to the document body, albeit with empty text nodes. Once the resource resolves, the `div`s are updated with the appropriate data. - -The downside of this approach is that the user is shown an empty component - let's see if we can do better than that in this next snippet: - -```jsx -const MyComponentWithShow = () => { - const [profile] = createResource(async () => { - /* fetcher code here */ - }); - return ( - fetching user data}> -
    {profile().name}
    -
    {profile().email}
    -
    - ); -}; -``` +- **Type:** `JSX.Element` -In this snippet, we first show a fallback when the resource hasn't resolved yet, and then switch to showing the profile data once it has. This results in a better user experience. +## Behavior -On the other hand, there is a slight downside to this approach. In our first example (using optional chaining), the divs were created immediately, and once the resource resolves all that is needed to be done is to fill in the text of the `div`s. But in our second example (using ``), the `div`s are only created once the resource has resolved, which means there is more work that needs to be done after the resource has resolved before the data can be shown to the user (of course, in this toy example the amount of DOM work is relatively trivial). +- `` is triggered when a resource is read inside the boundary. +- The boundary waits until pending tracked resources resolve. +- `` is non-blocking: the subtree still runs while the fallback is rendered. +- Nested suspense boundaries handle resources read under the nearest boundary. +- `onMount` and `createEffect` inside the suspended subtree run after the boundary resolves. -We can have the best of both worlds by using {""}: +## Examples -```jsx -const MyComponentWithSuspense = () => { - const [profile] = createResource(async () => { - /* fetcher code here */ - }); - return ( - fetching user data}> -
    {profile()?.name}
    -
    {profile()?.email}
    -
    - ); -}; -``` +### Basic usage + +```tsx +import { Suspense } from "solid-js"; -In this case, the `div`s are created immediately, but instead of being attached to the document body, the fallback is shown. Once the resource resolves, the text in the `div`s is updated, and then they are attached to the document (and the fallback removed). - -It is important to note that _the execution of the component does not pause_ when using suspense. Instead, when a resource is read under a suspense boundary, it ensures that the nodes are not attached to the document until after the resource has resolved. Suspense allows us to have the best of both worlds: do as much work as we can _before_ the resource resolves, and also show a fallback until then. - -With this in mind, we can understand that there isn't much gained from suspense in the following code: - -```jsx -const MyComponentWithSuspenseAndShow = () => { - const [profile] = createResource(async () => { - /* fetcher code here */ - }); - return ( - fetching user data}> - -
    {profile().name}
    -
    {profile().email}
    -
    -
    - ); -}; +}> + +; ``` -In this code, we don't create _any_ DOM nodes inside {""} before the resource resolves, so it is pretty much the same as the second example where we only used ``. +### Nested suspense -:::note -Suspense is triggered by reading a resource inside the {""}{" "} -boundary. Components wrapped with suspense still run fully, just as they would -without suspense. However, code wrapped in `onMount` and `createEffect` only -run after the resource resolves. -::: +```tsx +import { Suspense } from "solid-js"; -## Props +Loading page}> +

    {title()}

    + Loading details}> + {data()} + +
    ; +``` + +## Related -| Name | Type | Description | -| :--------- | :------------ | :--------------------------------------------------------------- | -| `fallback` | `JSX.Element` | The fallback component to render while the children are loading. | +- [``](/reference/components/suspense-list) +- [`createResource`](/reference/basic-reactivity/create-resource) From e2091bce7bbe047a5f1d7607f3b967c55d78377d Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Fri, 3 Apr 2026 14:01:54 -0700 Subject: [PATCH 10/88] migrate rendering reference pages and split isDev --- src/routes/reference/rendering/data.json | 1 + src/routes/reference/rendering/dev.mdx | 55 ++++++++--- src/routes/reference/rendering/hydrate.mdx | 84 ++++++++++++---- .../reference/rendering/hydration-script.mdx | 96 +++++++++++++++---- src/routes/reference/rendering/is-dev.mdx | 51 ++++++++++ src/routes/reference/rendering/is-server.mdx | 34 +++++-- 6 files changed, 263 insertions(+), 58 deletions(-) create mode 100644 src/routes/reference/rendering/is-dev.mdx diff --git a/src/routes/reference/rendering/data.json b/src/routes/reference/rendering/data.json index cd3ffdce0..3f811d6b3 100644 --- a/src/routes/reference/rendering/data.json +++ b/src/routes/reference/rendering/data.json @@ -4,6 +4,7 @@ "dev.mdx", "hydrate.mdx", "hydration-script.mdx", + "is-dev.mdx", "is-server.mdx", "render.mdx", "render-to-stream.mdx", diff --git a/src/routes/reference/rendering/dev.mdx b/src/routes/reference/rendering/dev.mdx index 29c51c8d1..7d5e3209c 100644 --- a/src/routes/reference/rendering/dev.mdx +++ b/src/routes/reference/rendering/dev.mdx @@ -1,37 +1,64 @@ --- title: DEV use_cases: >- - development debugging, build optimization, library development, conditional - code, dev-only features + development builds, development-only checks, conditional dev-only code tags: - development - debugging - builds - - optimization - conditional version: "1.0" description: >- - Access development-only features in SolidJS with the DEV export. Enable - additional checks and debugging tools that are removed in production. + Read the development-only `DEV` export from `solid-js`. --- +`DEV` is a development-only export from `solid-js`. + +## Import + ```ts import { DEV } from "solid-js"; +``` -const DEV: object | undefined; +## Type + +```ts +const DEV: + | { + readonly hooks: { + afterUpdate: (() => void) | null; + afterCreateOwner: ((owner: unknown) => void) | null; + afterCreateSignal: ((signal: unknown) => void) | null; + afterRegisterGraph: ((sourceMapValue: unknown) => void) | null; + }; + readonly writeSignal: (...args: unknown[]) => unknown; + readonly registerGraph: (...args: unknown[]) => unknown; + } + | undefined; ``` -On the client, Solid provides (via [conditional exports](https://nodejs.org/api/packages.html#conditional-exports)) different builds depending on whether the **development** condition is set. -Development mode provides some additional checking — e.g. detecting accidental use of multiple instances of Solid — which are removed in production builds. +## Value + +- **Type:** development-only object or `undefined` -If you want code to run only in development mode (most useful in libraries), you can check whether the **DEV** export is defined. -Note that it is always defined on the server, so you may want to combine with [isServer](/reference/rendering/is-server): +## Behavior + +- In development builds, `DEV` is defined and exposes development hooks. +- In production builds, `DEV` is `undefined`. + +## Examples + +### Basic usage ```ts -import { DEV } from "solid-js" -import { isServer } from "solid-js/web" +import { DEV } from "solid-js"; -if (DEV && !isServer) { - console.log(...); +if (DEV) { + console.log("development build"); } ``` + +## Related + +- [`isDev`](/reference/rendering/is-dev) +- [`isServer`](/reference/rendering/is-server) diff --git a/src/routes/reference/rendering/hydrate.mdx b/src/routes/reference/rendering/hydrate.mdx index c01962566..fa26080a9 100644 --- a/src/routes/reference/rendering/hydrate.mdx +++ b/src/routes/reference/rendering/hydrate.mdx @@ -1,24 +1,35 @@ --- title: hydrate use_cases: >- - ssr hydration, client initialization, server-rendered apps, spa startup, dom - rehydration + hydrating server-rendered HTML, attaching client behavior to an SSR subtree tags: - hydration - ssr - rendering - - initialization - dom version: "1.0" description: >- - Hydrate server-rendered HTML with SolidJS client-side code. Essential for - initializing SSR applications and attaching interactivity to static HTML. + Hydrate server-rendered HTML and attach Solid's client-side behavior to an + existing DOM subtree. --- +`hydrate` attaches Solid's client-side behavior to DOM that was already rendered on the server. + +## Import + ```ts import { hydrate } from "solid-js/web"; -import type { JSX } from "solid-js"; -import type { MountableElement } from "solid-js/web"; +``` + +## Type + +```ts +type MountableElement = + | Element + | Document + | ShadowRoot + | DocumentFragment + | Node; function hydrate( fn: () => JSX.Element, @@ -27,18 +38,57 @@ function hydrate( ): () => void; ``` -This method is similar to `render` except that it attempts to rehydrate what is already rendered to the DOM. -When initializing in the browser a page has already been server rendered. +## Parameters + +### `fn` + +- **Type:** `() => JSX.Element` + +Function that returns the root JSX to hydrate. + +### `node` + +- **Type:** `MountableElement` + +DOM node that contains the server-rendered markup. + +### `options` + +#### `renderId` + +- **Type:** `string` + +Hydration render identifier. + +#### `owner` + +- **Type:** `unknown` + +Owner used for the hydration root. + +## Return value + +- **Type:** `() => void` + +Dispose function for the hydrated root. + +## Behavior + +- `hydrate` reuses existing DOM instead of creating a new subtree. +- The target node is expected to contain server-rendered markup that matches the rendered output. +- The returned function disposes the hydrated root. + +## Examples + +### Basic usage ```ts -const dispose = hydrate(App, document.getElementById("app")); +import { hydrate } from "solid-js/web"; + +const dispose = hydrate(() => , document.getElementById("app")!); ``` -## Parameters +## Related -| Prop | type | description | -| ---------------- | ------------------- | ------------------------------------------- | -| fn | `() => JSX.Element` | Function that returns the application code. | -| node | MountableElement | DOM Element to mount the application to | -| options.renderId | string | | -| options.owner | unknown | | +- [`render`](/reference/rendering/render) +- [`HydrationScript`](/reference/rendering/hydration-script) diff --git a/src/routes/reference/rendering/hydration-script.mdx b/src/routes/reference/rendering/hydration-script.mdx index 41af5b0b1..8c661378b 100644 --- a/src/routes/reference/rendering/hydration-script.mdx +++ b/src/routes/reference/rendering/hydration-script.mdx @@ -1,38 +1,100 @@ --- title: hydrationScript use_cases: >- - ssr hydration, server-side rendering, initial page load optimization, - capturing events before js loads + server rendering, hydration bootstrap, capturing delegated events before + client hydration tags: - ssr - hydration - - performance - - events - bootstrap + - events version: "1.0" description: >- - Bootstrap client-side hydration in SSR apps with HydrationScript. Capture and - replay events before JavaScript loads for seamless user experience. + Emit the hydration bootstrap script with `HydrationScript` or + `generateHydrationScript`. --- +`HydrationScript` and `generateHydrationScript` emit Solid's hydration bootstrap script. + +## Import + ```ts -import { generateHydrationScript, HydrationScript } from "solid-js/web"; -import type { JSX } from "solid-js"; +import { HydrationScript, generateHydrationScript } from "solid-js/web"; +``` -function generateHydrationScript(options: { - nonce?: string; - eventNames?: string[]; -}): string; +## Type +```ts function HydrationScript(props: { nonce?: string; eventNames?: string[]; }): JSX.Element; + +function generateHydrationScript(options: { + nonce?: string; + eventNames?: string[]; +}): string; +``` + +## `HydrationScript` props + +### `nonce` + +- **Type:** `string` + +Nonce applied to the generated `