diff --git a/public/llms.txt b/public/llms.txt index f0cd0ce39..052c911fe 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -71,6 +71,7 @@ - [](https://docs.solidjs.com/reference/components/suspense) - [](https://docs.solidjs.com/reference/components/suspense-list) - [ / ](https://docs.solidjs.com/reference/components/switch-and-match) +- [createDynamic](https://docs.solidjs.com/reference/components/create-dynamic) - [@once](https://docs.solidjs.com/reference/jsx-attributes/once) - [attr:*](https://docs.solidjs.com/reference/jsx-attributes/attr) - [bool:*](https://docs.solidjs.com/reference/jsx-attributes/bool) @@ -103,6 +104,7 @@ - [DEV](https://docs.solidjs.com/reference/rendering/dev) - [hydrate](https://docs.solidjs.com/reference/rendering/hydrate) - [hydrationScript](https://docs.solidjs.com/reference/rendering/hydration-script) +- [isDev](https://docs.solidjs.com/reference/rendering/is-dev) - [isServer](https://docs.solidjs.com/reference/rendering/is-server) - [render](https://docs.solidjs.com/reference/rendering/render) - [renderToStream](https://docs.solidjs.com/reference/rendering/render-to-stream) @@ -125,6 +127,7 @@ - [Overview](https://docs.solidjs.com/solid-start/) - [Getting started](https://docs.solidjs.com/solid-start/getting-started) +- [Migrating from v1](https://docs.solidjs.com/solid-start/migrating-from-v1) - [Routing](https://docs.solidjs.com/solid-start/building-your-application/routing) - [API routes](https://docs.solidjs.com/solid-start/building-your-application/api-routes) - [CSS and styling](https://docs.solidjs.com/solid-start/building-your-application/css-and-styling) @@ -137,6 +140,7 @@ - [Sessions](https://docs.solidjs.com/solid-start/advanced/session) - [Request events](https://docs.solidjs.com/solid-start/advanced/request-events) - [Returning responses](https://docs.solidjs.com/solid-start/advanced/return-responses) +- [Serialization](https://docs.solidjs.com/solid-start/advanced/serialization) - [Auth](https://docs.solidjs.com/solid-start/advanced/auth) - [WebSocket endpoint](https://docs.solidjs.com/solid-start/advanced/websocket) - [Security](https://docs.solidjs.com/solid-start/guides/security) @@ -182,6 +186,7 @@ - [Revalidation](https://docs.solidjs.com/solid-router/data-fetching/revalidation) - [Preload data](https://docs.solidjs.com/solid-router/data-fetching/how-to/preload-data) - [Handle pending and error states](https://docs.solidjs.com/solid-router/data-fetching/how-to/handle-error-and-loading-states) +- [Preloading](https://docs.solidjs.com/solid-router/advanced-concepts/preloading) - [Lazy loading](https://docs.solidjs.com/solid-router/advanced-concepts/lazy-loading) - [Migration from v0.9.x](https://docs.solidjs.com/solid-router/guides/migration) - [A](https://docs.solidjs.com/solid-router/reference/components/a) @@ -225,3 +230,4 @@ - [MetaProvider](https://docs.solidjs.com/solid-meta/reference/meta/metaprovider) - [Style](https://docs.solidjs.com/solid-meta/reference/meta/style) - [Title](https://docs.solidjs.com/solid-meta/reference/meta/title) +- [useHead](https://docs.solidjs.com/solid-meta/reference/meta/use-head) diff --git a/src/routes/reference/component-apis/children.mdx b/src/routes/reference/component-apis/children.mdx index d4f79c895..54da34705 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 a stable accessor for the resolved result. ## Import @@ -27,10 +24,11 @@ import { children } from "solid-js"; ## Type ```ts -function children(fn: Accessor): ChildrenReturn; +type ResolvedJSXElement = Exclude; +type ResolvedChildren = ResolvedJSXElement | ResolvedJSXElement[]; -type ChildrenReturn = Accessor & { - toArray: () => ResolvedChildren[]; +function children(fn: Accessor): Accessor & { + toArray: () => ResolvedJSXElement[]; }; ``` @@ -41,24 +39,20 @@ 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` +- **Type:** `Accessor & { toArray: () => ResolvedJSXElement[] }` -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. +- The returned accessor memoizes the resolved children, so repeated reads use the resolved result instead of recreating the child structure. +- `children` resolves nested arrays, fragments, and zero-argument child accessors from `props.children`. +- `toArray()` returns the resolved children as an array. It returns `[]` when the resolved value is `null` or `undefined`. ## Examples @@ -68,53 +62,49 @@ This method is exposed on the returned accessor and is useful for iteration or i function Wrapper(props) { const resolved = children(() => props.children); - return
{resolved()}
; + return ( +
+
{resolved()}
+
{resolved()}
+
+ ); } -// Usage - - one - two -; +function Example() { + return ( + + First + Second + + ); +} ``` -### `.toArray()` example +### Flatten children into an array ```tsx function List(props) { const resolved = children(() => props.children); - const items = resolved.toArray(); return (
    - {items.map((child) => ( + {resolved.toArray().map((child) => (
  • {child}
  • ))}
); } -// Usage - - one - two -; +function Example() { + return ( + + Alpha + Beta + + ); +} ``` -> [!NOTE] -> `children` resolves the current value of `props.children`. -> If `props.children` is reactive, the resolved accessor reflects updates. - -### Working with function-as-children +## Related -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}; -``` +- [Props](/concepts/components/props) diff --git a/src/routes/reference/component-apis/create-context.mdx b/src/routes/reference/component-apis/create-context.mdx index 37070da49..c6fa4e8be 100644 --- a/src/routes/reference/component-apis/create-context.mdx +++ b/src/routes/reference/component-apis/create-context.mdx @@ -12,103 +12,101 @@ 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 an optional `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: +function createContext( + defaultValue: T, + options?: { name?: string } +): Context; +``` -```ts title="/context/counter.ts" -import { createContext } from "solid-js"; +## Parameters -export const INITIAL_COUNT = 0; +### `defaultValue` -const INITIAL_STORE_SETTER = { - increment: () => {}, - decrement: () => {}, -}; +- **Type:** `T` -export const CounterContext = createContext([ - { count: INITIAL_COUNT }, - INITIAL_STORE_SETTER, -]); -``` +Default value returned by [`useContext`](/reference/component-apis/use-context) when no matching provider is found. -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} - - ) -} -``` +### `options` + +#### `name` + +- **Type:** `string` -A few imporant notes on how to pass data through the context API: +Debug name used by development tooling. -- 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. +## Return value -To learn how to consume the context, see the [useContext](/reference/component-apis/use-context) documentation and the [Context concepts entry](/concepts/context). +- **Type:** `Context` or `Context` -## Default Values +Returns a context object containing `id`, `Provider`, and `defaultValue`. -`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. +## Behavior -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. +- `Context.Provider` passes its `value` prop to descendant calls to [`useContext`](/reference/component-apis/use-context). +- `useContext` reads the nearest matching provider in the current owner tree. +- If no matching provider is found, `useContext` returns `defaultValue` when one was supplied, or `undefined` otherwise. -This has implications for TS. -If no `defaultValue` is passed, then it is possible that `useContext()` will return `undefined`, and the types reflect this. +## Notes -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. +- 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. -## Type signature +## 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"; -function createContext(defaultValue?: T): Context; +type Theme = "light" | "dark"; + +const ThemeContext = createContext("light"); ``` + +### Create a provider + +```tsx +import { createContext } from "solid-js"; + +const CounterContext = createContext<{ count: number }>(); + +function CounterProvider(props) { + return ( + + {props.children} + + ); +} +``` + +## Related + +- [`useContext`](/reference/component-apis/use-context) +- [Context](/concepts/context) diff --git a/src/routes/reference/component-apis/create-unique-id.mdx b/src/routes/reference/component-apis/create-unique-id.mdx index fbc2bdeab..a2de7d756 100644 --- a/src/routes/reference/component-apis/create-unique-id.mdx +++ b/src/routes/reference/component-apis/create-unique-id.mdx @@ -11,12 +11,11 @@ tags: - utilities version: "1.0" description: >- - Generate unique IDs that are stable across server and client with - createUniqueId. Essential for accessible forms and SSR-compatible components. + Generate a unique string for the current render or hydration context. --- -The `createUniqueId` function generates a unique ID that remains consistent across both server and client renders. -It is commonly used with HTML `id` and `for` attributes to ensure stable hydration. +`createUniqueId` generates a unique string for the current render or hydration context. +During hydration, matching server and client call order produces matching IDs. `createUniqueId` does _not_ generate a cryptographically secure ID and is not suitable for security-sensitive data. Additionally, it should not be used in scenarios that require uniqueness across a distributed system. @@ -48,6 +47,11 @@ This function does not take any parameters. `createUniqueId` returns a unique `string` that is stable across server and client renders. +## Behavior + +- During hydration, IDs come from the current hydration context, so matching call order produces matching server and client IDs. +- Outside hydration context, client-side IDs use a local counter and are unique only within the current Solid runtime instance. + ## Examples ### Basic Usage @@ -57,9 +61,17 @@ import { createUniqueId } from "solid-js"; type InputProps = { id?: string; + label: string; }; function Input(props: InputProps) { - return ; + const inputId = props.id ?? createUniqueId(); + + return ( + <> + + + + ); } ``` diff --git a/src/routes/reference/component-apis/lazy.mdx b/src/routes/reference/component-apis/lazy.mdx index 87c5ba0b7..970d510fd 100644 --- a/src/routes/reference/component-apis/lazy.mdx +++ b/src/routes/reference/component-apis/lazy.mdx @@ -12,12 +12,11 @@ tags: - imports version: "1.0" description: >- - Lazy load components with code splitting to reduce bundle size and improve - performance. Components load on-demand and integrate with Suspense. + Create a component that loads its module lazily from a dynamic import. --- -The `lazy` helper wraps a dynamic import and returns a component that loads on demand. -Lazy components accept the same props as their eager counterparts and integrate with `` boundaries. +`lazy` wraps a dynamic import and returns a component that loads its module on demand. +The returned component accepts the same props as the loaded component and exposes `preload()`. ## Import @@ -27,10 +26,10 @@ import { lazy } from "solid-js"; ## Type -```tsx +```ts function lazy>( fn: () => Promise<{ default: T }> -): T & { preload: () => Promise }; +): T & { preload: () => Promise<{ default: T }> }; ``` ## Parameters @@ -44,32 +43,42 @@ Dynamic import that resolves to the component module, exposing the component as ## Return value -`lazy` returns a renderable component compatible with `T`. -The component exposes a `preload()` method that resolves the underlying module. +- **Type:** `T & { preload: () => Promise<{ default: T }> }` + +Returns a renderable component compatible with `T`. +The returned component also exposes `preload()`. + +`preload()` starts loading the module without rendering the component and returns the resolved module. -| Property | Type | Description | -| --------- | ------------------ | ---------------------------------------------------------------------- | -| `preload` | `() => Promise` | Loads the module without rendering and returns the resolved component. | +## Behavior + +- Rendering a lazy component starts loading its module if it has not already been loaded. +- Inside a [``](/reference/components/suspense) boundary, the boundary shows its fallback while the module is loading. +- Without a suspense boundary, the lazy component renders an empty string until the module resolves. +- `preload()` starts the same module request without rendering the component. ## Examples ### Basic usage -```tsx title="app.tsx" -import { lazy } from "solid-js"; +```tsx +import { lazy, Suspense } from "solid-js"; const ComponentA = lazy(() => import("./ComponentA")); function App(props: { title: string }) { - return ; + return ( + Loading...

}> + +
+ ); } ``` ### Preloading nested lazy components ```tsx -import { lazy } from "solid-js"; -import type { Component } from "solid-js"; +import { createSignal, lazy, Show, Suspense } from "solid-js"; const Nested = lazy(() => import("./Nested")); @@ -85,14 +94,16 @@ const ComponentWithPreload = () => { Preload Nested Component - + Loading nested component...

}> + +
); }; ``` -## See also +## Related -- [`Suspense`](https://docs.solidjs.com/reference/components/suspense) +- [`Suspense`](/reference/components/suspense) - [Router preloading guide](/solid-router/advanced-concepts/preloading) diff --git a/src/routes/reference/component-apis/use-context.mdx b/src/routes/reference/component-apis/use-context.mdx index aa8160eb1..0f9be9aeb 100644 --- a/src/routes/reference/component-apis/use-context.mdx +++ b/src/routes/reference/component-apis/use-context.mdx @@ -11,40 +11,89 @@ 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` reads the nearest provider value for a context object in the current owner tree. + +## Import ```ts -const [state, { increment, decrement }] = useContext(CounterContext); +import { useContext } from "solid-js"; ``` -## Recommended usage +## Type -It is often a good idea to wrap `useContext` in a function like so: +```ts +interface Context { + id: symbol; + Provider: (props: { value: T; children: any }) => any; + defaultValue: T; +} -```ts title="/context/counter-component.tsx" -function useCounterContext() { - const context = useContext(CounterContext); +function useContext(context: Context): T; +``` - if (!context) { - throw new Error("useCounterContext: cannot find a CounterContext"); - } +## Parameters - return context; +### `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 the context was created without a default value, `T` can include `undefined`. +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. +- If no matching provider is found, it returns the default value from [`createContext`](/reference/component-apis/create-context), or `undefined` when no default value was supplied. +- A provider value of `undefined` is treated the same as a missing provider and 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}; } ``` -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. +### Throw when a provider is missing -## Type signature +This example checks for `undefined` when the context was created without a default value. ```ts -import { type Context } from "solid-js"; +import { createContext, useContext } from "solid-js"; -function useContext(context: Context): T; +const CounterContext = createContext(); + +function useCounterContext() { + const context = useContext(CounterContext); + + if (context === undefined) { + throw new Error("CounterContext is missing"); + } + + return context; +} ``` + +## Related + +- [`createContext`](/reference/component-apis/create-context) +- [Context](/concepts/context) diff --git a/src/routes/reference/components/create-dynamic.mdx b/src/routes/reference/components/create-dynamic.mdx new file mode 100644 index 000000000..9f6c63749 --- /dev/null +++ b/src/routes/reference/components/create-dynamic.mdx @@ -0,0 +1,92 @@ +--- +title: createDynamic +use_cases: >- + dynamic components, runtime component selection, polymorphic components, + lower-level rendering helpers, library components +tags: + - dynamic + - components + - jsx + - polymorphic + - rendering +version: "1.0" +description: >- + Render a component or intrinsic element selected at runtime from an accessor. +--- + +`createDynamic` renders the value returned by its `component` accessor as either a custom component or an intrinsic element. + +## Import + +```ts +import { createDynamic } from "solid-js/web"; +``` + +## Type + +```ts +type ValidComponent = + | keyof JSX.IntrinsicElements + | ((props: any) => JSX.Element); + +function createDynamic( + component: () => T | undefined, + props: ComponentProps +): JSX.Element; +``` + +## Parameters + +### `component` + +- **Type:** `() => T | undefined` + +Accessor that returns the component or intrinsic element to render. + +### `props` + +- **Type:** props accepted by the rendered component or element + +Props passed to the rendered component or element. + +## Return value + +- **Type:** `JSX.Element` + +Returns the rendered component or element. + +## Behavior + +- If `component()` is `undefined`, nothing is rendered. + +## Example + +```tsx +import { createSignal } from "solid-js"; + +const views = { + red: (props: { label: string }) => ( +

{props.label}

+ ), + blue: (props: { label: string }) => ( +

{props.label}

+ ), +}; + +function App() { + const [selected, setSelected] = createSignal("red"); + + return ( + <> + + + + {createDynamic(() => views[selected()], { label: "Selected view" })} + + ); +} +``` + +## Related + +- [``](/reference/components/dynamic) diff --git a/src/routes/reference/components/data.json b/src/routes/reference/components/data.json index 716b071e6..18de882d2 100644 --- a/src/routes/reference/components/data.json +++ b/src/routes/reference/components/data.json @@ -1,6 +1,7 @@ { "title": "Components", "pages": [ + "create-dynamic.mdx", "dynamic.mdx", "error-boundary.mdx", "for.mdx", diff --git a/src/routes/reference/components/dynamic.mdx b/src/routes/reference/components/dynamic.mdx index ab3c721d6..0b56f0d51 100644 --- a/src/routes/reference/components/dynamic.mdx +++ b/src/routes/reference/components/dynamic.mdx @@ -2,45 +2,95 @@ 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. --- -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. -```tsx +## Import + +```ts 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; ``` -Here's an example of how you can use it: +## Type -```tsx - +```ts +type ValidComponent = + | keyof JSX.IntrinsicElements + | ((props: any) => JSX.Element); + +type DynamicProps> = { + [K in keyof P]: P[K]; +} & { + component: T | undefined; +}; + +function Dynamic(props: DynamicProps): JSX.Element; ``` ## Props -| 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. | +### `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`. + +## Return value + +- **Type:** `JSX.Element` + +Returns the rendered component or element. + +## Behavior + +- When `component` is `undefined`, nothing is rendered. + +## Example + +```tsx +import { createSignal } from "solid-js"; + +const views = { + red: (props: { label: string }) => ( +

{props.label}

+ ), + blue: (props: { label: string }) => ( +

{props.label}

+ ), +}; + +function App() { + const [selected, setSelected] = createSignal("red"); + + return ( + <> + + + + + + ); +} +``` + +## Related + +- [`createDynamic`](/reference/components/create-dynamic) diff --git a/src/routes/reference/components/error-boundary.mdx b/src/routes/reference/components/error-boundary.mdx index e3c295ae5..d91e14120 100644 --- a/src/routes/reference/components/error-boundary.mdx +++ b/src/routes/reference/components/error-boundary.mdx @@ -2,67 +2,84 @@ 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` + +Subtree wrapped by the boundary. -`fallback` provides content to display when an error occurs. -If a function is passed, it receives two parameters: +## Return value -- `err`: The caught error object. -- `reset`: A function that forces the `` to re-render its children and clear the error state. +- **Type:** `JSX.Element` -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. +Returns the rendered children or fallback content. -## Example +## Behavior + +- `` catches errors thrown while rendering JSX and while updating reactive computations in its subtree, including reactive async primitives when they surface through the same rendering or update flow. +- 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}

- -
- )} - > - -
- ); +function ErrorProne() { + throw new Error("Broken"); } + + ( +
+

{String(error)}

+ +
+ )} +> + +
; ``` + +## Related + +- [`catchError`](/reference/reactive-utilities/catch-error) diff --git a/src/routes/reference/components/for.mdx b/src/routes/reference/components/for.mdx index 2128f18a8..75ec4f86a 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 an empty array, `undefined`, `null`, or `false`. -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: +### `children` -```jsx -{(item) =>
{item}
}
+- **Type:** `(item: T[number], index: Accessor) => U` + +Child function. It receives the current item and an index accessor. + +## Return value + +- **Type:** `JSX.Element` + +## Behavior + +- `` maps items by value identity. +- If the same item value appears at a new position, its rendered node can be moved instead of recreated. +- The `index` argument is an accessor. +- `` uses [`mapArray`](/reference/reactive-utilities/map-array) internally. + +## Examples + +### Basic usage + +```tsx +const items = ["A", "B", "C"]; + +No items}> + {(item) =>
{item}
} +
; ``` -The optional second argument is an index signal: +### Access the index + +```tsx +const items = ["A", "B", "C"]; -```jsx -Loading...}> + {(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..3a1fb7030 100644 --- a/src/routes/reference/components/index-component.mdx +++ b/src/routes/reference/components/index-component.mdx @@ -1,75 +1,100 @@ --- title: use_cases: >- - primitive arrays, non-keyed lists, index-based rendering, static positions, + non-keyed lists, index-based rendering, fixed positions, changing values, simple lists tags: - lists - iteration - components - arrays - - primitives - 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 an empty array, `undefined`, `null`, or `false`. + +### `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. +- When the array is reordered, rendered positions stay tied to indexes, and `item()` updates to the current value at that index. +- `` uses [`indexArray`](/reference/reactive-utilities/index-array) internally. + +## Examples + +### Basic usage ```tsx -Loading...}> +const items = ["A", "B", "C"]; + +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...}> +const items = ["A", "B", "C"]; + + {(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/no-hydration.mdx b/src/routes/reference/components/no-hydration.mdx index eb142215f..94104e743 100644 --- a/src/routes/reference/components/no-hydration.mdx +++ b/src/routes/reference/components/no-hydration.mdx @@ -1,42 +1,66 @@ --- 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 - -```tsx +```ts import { NoHydration } from "solid-js/web"; -import { InteractiveComponent, StaticContent } from "./components"; +``` + +## Type + +```ts +function NoHydration(props: { children?: JSX.Element }): JSX.Element; +``` + +## Props + +### `children` + +- **Type:** `JSX.Element` +Content inside the boundary. + +## Return value + +- **Type:** `JSX.Element` + +Returns the children during server and client-only rendering. + +## Behavior + +- During server rendering, children inside `` render normally. +- During client hydration, Solid leaves the existing server-rendered DOM in place and does not hydrate that subtree. +- In client-only rendering, `` renders its children normally. +- Interactive behavior inside the boundary does not hydrate on the client. + +## Examples + +### Basic usage + +```tsx function Example() { return (
- + - +
+ Rendered on the server without hydration +
); diff --git a/src/routes/reference/components/portal.mdx b/src/routes/reference/components/portal.mdx index 1dffac6a5..432ae43a7 100644 --- a/src/routes/reference/components/portal.mdx +++ b/src/routes/reference/components/portal.mdx @@ -1,52 +1,120 @@ --- 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` + +- **Type:** `HTMLDivElement | SVGGElement | ((el: HTMLDivElement | SVGGElement) => void)` -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. +Receives the created portal container element. This is a `
` by default or a `` when `isSVG` is `true`. + +### `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. +- During server rendering, `` returns no output. +- Portals render only on the client and are skipped during hydration. + +## Examples + +### Basic usage ```tsx - -
My Content
-
-``` +import { createSignal, Show } from "solid-js"; -## Props +function App() { + const [open, setOpen] = createSignal(false); + + return ( + <> + -| 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. | + + +
+
Popup
+ +
+
+
+ + ); +} +``` diff --git a/src/routes/reference/components/show.mdx b/src/routes/reference/components/show.mdx index 7f51f9ca6..dd0cf37da 100644 --- a/src/routes/reference/components/show.mdx +++ b/src/routes/reference/components/show.mdx @@ -13,92 +13,107 @@ 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` + +- **Type:** `JSX.Element` -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. +Content rendered when `when` is falsy. + +### `children` + +- **Type:** `JSX.Element | ((item: Accessor>) => JSX.Element) | ((item: NonNullable) => JSX.Element)` + +Content rendered when `when` is truthy. + +## Return value + +- **Type:** `JSX.Element` + +Returns the rendered children or `fallback` content. + +## Behavior + +- With `keyed` omitted or `false`, `` updates only when the truthiness of `when` changes. Replacing one truthy value with another truthy value does not recreate the child block, and function children receive an accessor that can only be read while the condition remains truthy. +- With `keyed={true}`, changes to the `when` value trigger a new render even when the value remains truthy, and function children receive the value directly. +- Function children are wrapped in [`untrack`](/reference/reactive-utilities/untrack). + +## 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()}

-
-
- ); -} +const loading = () => false; + +Loading...
}> +
Loaded
+; ``` -## 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 +const user = () => ({ name: "Ada" }); -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()}} -
- ); -} +const user = () => ({ name: "Ada" }); + + + {(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/suspense-list.mdx b/src/routes/reference/components/suspense-list.mdx index fa7754279..68604cca2 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,24 @@ 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. +:::[warning] +This is an experimental component +::: + +`` coordinates how multiple child suspense boundaries reveal their content. + +## 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 +38,79 @@ function SuspenseList(props: { }): JSX.Element; ``` -**Note**: SuspenseList is still in the experimental stage and does not have full `SSR` support. - -Here's an example of how to use SuspenseList: +## Props -```tsx - - - Loading posts...}> - - - Loading fun facts...}> - - - -``` +### `children` -## 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 in ordered reveal modes. + +## Return value + +- **Type:** `JSX.Element` + +Returns the coordinated suspense boundaries. + +## 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"`, the first still-pending item can keep its fallback visible, while later pending items hide theirs. +- With `tail="hidden"`, pending items after the first still-pending item hide their fallbacks. +- `` coordinates child suspense boundaries. It does not suspend by itself. + +## Examples + +### Basic usage + +```tsx +import { createResource, Suspense } from "solid-js"; + +const delayed = async (value: T, ms: number) => { + await new Promise((resolve) => setTimeout(resolve, ms)); + return value; +}; + +function ProfileDetails() { + const [user] = createResource(() => delayed({ name: "Ada" }, 500)); + return

{user()?.name}

; +} + +function ProfileTimeline() { + const [posts] = createResource(() => delayed(["First post"], 1000)); + return ( +
    + {posts()?.map((post) => ( +
  • {post}
  • + ))} +
+ ); +} + + + 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..a192b2cc8 100644 --- a/src/routes/reference/components/suspense.mdx +++ b/src/routes/reference/components/suspense.mdx @@ -2,157 +2,119 @@ 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 suspense-tracked async dependencies 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. +`` renders `fallback` while suspense-tracked async dependencies under its boundary are pending. -```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 suspense-tracked async dependencies 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` + +Returns the rendered subtree or `fallback` content depending on pending suspense-tracked async dependencies. -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). +- When a suspense-tracked async dependency is read inside the boundary, `` renders `fallback` until the pending work resolves. +- `` is non-blocking: the subtree can continue running and create reactive owners before the boundary reveals the resolved content in the DOM. +- Nested suspense boundaries handle suspense-tracked async dependencies 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 { createResource } from "solid-js"; + +const fetchMessage = async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + return "Loaded"; }; + +function AsyncMessage() { + const [message] = createResource(fetchMessage); + return

{message()}

; +} + +Loading...

}> + +
; ``` -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}
-
-
- ); +### Nested suspense + +```tsx +import { createResource } from "solid-js"; + +const fetchTitle = async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + return "Profile"; }; -``` -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 ``. +const fetchDetails = async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + return "Details loaded"; +}; -:::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. -::: +function Title() { + const [title] = createResource(fetchTitle); + return

{title()}

; +} -## Props +function Details() { + const [details] = createResource(fetchDetails); + return

{details()}

; +} + +Loading page...}> + + <Suspense fallback={<div>Loading details...</div>}> + <Details /> + </Suspense> +</Suspense>; +``` + +## Related -| Name | Type | Description | -| :--------- | :------------ | :--------------------------------------------------------------- | -| `fallback` | `JSX.Element` | The fallback component to render while the children are loading. | +- [`<SuspenseList>`](/reference/components/suspense-list) +- [`createResource`](/reference/basic-reactivity/create-resource) diff --git a/src/routes/reference/components/switch-and-match.mdx b/src/routes/reference/components/switch-and-match.mdx index 98d1dcbfa..cbcb1384b 100644 --- a/src/routes/reference/components/switch-and-match.mdx +++ b/src/routes/reference/components/switch-and-match.mdx @@ -13,70 +13,119 @@ 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. +`<Switch>` renders the first child `<Match>` whose `when` prop is truthy. `<Match>` can render JSX directly or accept a function child. + +## Import ```ts -import { Switch, Match } from "solid-js"; -import type { MatchProps, JSX } from "solid-js"; +import { Match, Switch } from "solid-js"; +``` + +## Type + +```ts +type Accessor<T> = () => T; function Switch(props: { fallback?: JSX.Element; children: JSX.Element; -}): () => JSX.Element; +}): JSX.Element; + +function Match<T>(props: { + when: T | undefined | null | false; + keyed?: false; + children: JSX.Element | ((item: Accessor<NonNullable<T>>) => JSX.Element); +}): JSX.Element; -type MatchProps<T> = { +function Match<T>(props: { when: T | undefined | null | false; - children: JSX.Element | ((item: T) => JSX.Element); -}; -function Match<T>(props: MatchProps<T>); + keyed: true; + children: JSX.Element | ((item: NonNullable<T>) => JSX.Element); +}): JSX.Element; ``` -A super simple implementation of this component would be: +## `<Switch>` 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 `<Match>` has a truthy `when` value. - return props.fallback; -} -``` +### `children` -For example, it can be used to perform basic routing: +- **Type:** `JSX.Element` -```tsx -<Switch fallback={<div>Not Found</div>}> - <Match when={state.route === "home"}> - <Home /> - </Match> - <Match when={state.route === "settings"}> - <Settings /> - </Match> -</Switch> -``` +Child `<Match>` elements. + +## `<Match>` props + +### `when` + +- **Type:** `T | undefined | null | false` -Match also supports function children to serve as keyed flow. +Condition value for the branch. -## Props +### `keyed` -### Switch +- **Type:** `boolean` -| Name | Type | Default | Description | -| ---------- | ------------- | ----------- | -------------------------------------------------------------------------------- | -| `fallback` | `JSX.Element` | `undefined` | The fallback element to render if no `Match` component has a truthy `when` prop. | +Controls whether function children receive the current value directly instead of an accessor. + +### `children` + +- **Type:** `JSX.Element | ((item: Accessor<NonNullable<T>>) => JSX.Element) | ((item: NonNullable<T>) => JSX.Element)` + +Content rendered when the branch matches. + +## Return value + +- **Type:** `JSX.Element` + +Returns the selected branch or the `fallback` content. + +## Behavior + +- `<Switch>` evaluates its child `<Match>` elements in order and renders only the first truthy branch. +- If no branch matches, `<Switch>` renders `fallback`. +- Function children in `<Match>` are wrapped in [`untrack`](/reference/reactive-utilities/untrack). +- With `keyed={false}`, function children in `<Match>` receive an accessor that can only be read while that branch remains selected. Replacing one truthy value with another truthy value does not recreate the child block. +- With `keyed={true}`, function children receive the current value directly, and changing the `when` value recreates the child block even when it remains truthy. + +## Examples + +### Basic usage + +```tsx +import { createSignal } from "solid-js"; + +const [status, setStatus] = createSignal<"loading" | "success" | "error">( + "loading" +); + +<> + <button onClick={() => setStatus("loading")}>Loading</button> + <button onClick={() => setStatus("success")}>Success</button> + <button onClick={() => setStatus("error")}>Error</button> + + <Switch fallback={<p>Unknown status</p>}> + <Match when={status() === "loading"}> + <p>Loading...</p> + </Match> + <Match when={status() === "success"}> + <p>Saved</p> + </Match> + <Match when={status() === "error"}> + <p>Failed</p> + </Match> + </Switch> +</>; +``` -### 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. | +- [`<Show>`](/reference/components/show) +- [`untrack`](/reference/reactive-utilities/untrack) diff --git a/src/routes/reference/jsx-attributes/attr.mdx b/src/routes/reference/jsx-attributes/attr.mdx index 8352c7d46..5775c4801 100644 --- a/src/routes/reference/jsx-attributes/attr.mdx +++ b/src/routes/reference/jsx-attributes/attr.mdx @@ -1,29 +1,50 @@ --- title: "attr:*" use_cases: >- - web components, custom elements, forcing attributes, html attributes, dom - properties + custom elements, forcing attribute assignment, writing HTML attributes tags: - attributes - - web-components - dom - - props - - typescript - custom-elements version: "1.0" description: >- - Force props as HTML attributes instead of properties in SolidJS. Essential for - Web Components and custom element attribute handling. + Force a JSX key to be written as an attribute instead of a property. --- -Forces the prop to be treated as an attribute instead of a property. -Useful for Web Components where you want to set attributes. - -```tsx -<my-element attr:status={props.status} /> -``` +`attr:*` forces a JSX key to be written as an attribute instead of a property. :::note[Strong-Typing Custom Attributes] Type definitions are required when using TypeScript. See the[TypeScript](/configuration/typescript#forcing-properties-and-custom-attributes) page for examples. ::: + +## Syntax + +```tsx +<my-element attr:status={value} /> +``` + +## Value + +- **Type:** attribute value + +Value passed to normal attribute serialization after the `attr:` prefix is removed. + +## Behavior + +- `attr:name={value}` writes the value to the `name` attribute after the `attr:` prefix is removed. +- In the DOM runtime, `attr:name={undefined}` or `attr:name={null}` removes the attribute. +- In SSR output, the stripped attribute name and escaped value are written as HTML. + +## Examples + +### Basic usage + +```tsx +<my-element attr:status={props.status} /> +``` + +## Related + +- [`prop:*`](/reference/jsx-attributes/prop) +- [`bool:*`](/reference/jsx-attributes/bool) diff --git a/src/routes/reference/jsx-attributes/bool.mdx b/src/routes/reference/jsx-attributes/bool.mdx index 4b2699e70..6acd955b2 100644 --- a/src/routes/reference/jsx-attributes/bool.mdx +++ b/src/routes/reference/jsx-attributes/bool.mdx @@ -1,39 +1,59 @@ --- title: "bool:*" use_cases: >- - web components, custom elements, conditional attributes, boolean attributes, - dynamic attribute presence + boolean attributes, custom elements, controlling attribute presence tags: - attributes - - web-components - boolean - - conditional - - dynamic + - custom-elements version: "1.0" description: >- - Control attribute presence with bool:* directive in SolidJS. Add or remove - attributes dynamically based on truthy/falsy values for web components. + Control whether an attribute is present by writing through `bool:*`. --- -`bool:*` controls the presence of an attribute in an element. -When the value is `truthy` it adds the `attribute` to the element. -Alternatively, when the value is `falsy` it removes the `attribute` from the element. -This attribute is most useful for Web Components. +`bool:*` controls whether an attribute is present on an element. + +:::note[Strong-Typing Custom Boolean Attributes] +Type definitions are required when using TypeScript. +See the [TypeScript](/configuration/typescript#forcing-properties-and-custom-attributes) page for examples. +::: + +## Syntax ```tsx -<my-element bool:status={prop.value} /> +<my-element bool:status={value} /> ``` +## Value + +- **Type:** any truthy or falsy value + +Any JavaScript value is coerced by truthiness. + +## Behavior + +- `bool:name={value}` writes `name=""` when `value` is truthy. +- `bool:name={value}` removes `name` when `value` is falsy. +- SSR output follows the same presence-or-absence behavior. + +## Examples + +### Basic usage + +```tsx +<my-element bool:status={props.value} /> +``` + +### Resulting markup + ```tsx -// Assuming `prop.value` is `truthy`, then it becomes +// props.value is truthy <my-element status /> -// And when `falsy`, then it becomes +// props.value is falsy <my-element /> - ``` -:::note[Strong-Typing Custom Boolean Attributes] -Type definitions are required when using TypeScript. -See the [TypeScript](/configuration/typescript#forcing-properties-and-custom-attributes) page for examples. -::: +## Related + +- [`attr:*`](/reference/jsx-attributes/attr) diff --git a/src/routes/reference/jsx-attributes/classlist.mdx b/src/routes/reference/jsx-attributes/classlist.mdx index 679274322..16515a269 100644 --- a/src/routes/reference/jsx-attributes/classlist.mdx +++ b/src/routes/reference/jsx-attributes/classlist.mdx @@ -2,41 +2,48 @@ title: classList order: 1 use_cases: >- - styling components, dynamic classes, conditional styles, toggling classes, - reactive styling + toggling classes, conditional classes, reactive class updates tags: - styling - css - classes - reactive - - conditional - - dom version: "1.0" description: >- - Manage element classes dynamically in SolidJS with class and classList - attributes. Toggle multiple classes reactively based on application state. + Toggle element classes from an object of class names and boolean values. --- -Solid offers two attributes to set the class of an element: `class` and `classList`. +:::warning +`className` was deprecated in Solid 1.4 in favor of `class`. +::: + +`classList` toggles element classes from an object of class names and boolean values. -First, `class` can be set like other attributes. For example: +## Syntax ```tsx -// Two static classes -<div class="active editing" /> +<div classList={{ active: state.active }} /> +``` -// One dynamic class, deleting class attribute if it's not needed -<div class={state.active ? 'active' : undefined} /> +## Value -// Two dynamic classes -<div class={`${state.active ? 'active' : ''} ${state.currentId === row.id ? 'editing' : ''}`} /> -``` +- **Type:** `Record<string, boolean | undefined>` -:::note -Note that `className` was deprecated in Solid 1.4 in favor of {" "} `class`. -::: +Object whose keys are class names and whose values control whether each class is present. -Alternatively, the `classList` pseudo-attribute lets you specify an object, where each key is a class and the value is treated as a boolean representing whether to include that class. For example (matching the last example): +## Behavior + +- Each key is treated as a class name. +- Keys can contain multiple space-separated class names, which are toggled individually. +- Truthy values add the class and falsy values remove it. +- Updates are applied per class rather than replacing the whole `class` attribute. +- `classList` works through normal DOM prop assignment, including prop spreads and `<Dynamic>` when it renders an intrinsic element. +- If both `class` and `classList` are reactive, updates to `class` can overwrite classes managed by `classList`. +- SSR output merges `class`, `className`, and `classList` into the emitted `class` attribute. + +## Examples + +### Basic usage ```tsx <div @@ -47,23 +54,12 @@ Alternatively, the `classList` pseudo-attribute lets you specify an object, wher /> ``` -This example compiles to a render effect that dynamically calls [element.classList.toggle](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle) to turn each class on or off, only when the corresponding boolean changes. For example, when `state.active` becomes true [false], the element gains [loses] the `active` class. - -The value passed into `classList` can be any expression (including a signal getter) that evaluates to an appropriate object. Some examples: +### Dynamic class name ```tsx -// Dynamic class name and value <div classList={{ [className()]: classOn() }} /> - -// Signal class list -const [classes, setClasses] = createSignal({}) -setClasses((c) => ({ ...c, active: true })) -<div classList={classes()} /> ``` -While possible, mixing `class` and `classList` in Solid can lead to unexpected behavior. -The safest approach is to use a static string (or nothing) for `class` and keep `classList` reactive. -You can also use a static computed value for class, such as `class={baseClass()}`, however you must make sure it comes before any `classList` pseudo-attributes. -If both `class` and `classList` are reactive, changes to `class` will overwrite the entire `class` attribute, potentially undoing any updates made by `classList`. +## Related -Because classList is a compile-time pseudo-attribute, it does not work in a prop spread like `<div {...props} />` or in `<Dynamic>`. +- [`class`](/reference/jsx-attributes/class) diff --git a/src/routes/reference/jsx-attributes/innerhtml.mdx b/src/routes/reference/jsx-attributes/innerhtml.mdx index 54083560d..313d60592 100644 --- a/src/routes/reference/jsx-attributes/innerhtml.mdx +++ b/src/routes/reference/jsx-attributes/innerhtml.mdx @@ -1,25 +1,50 @@ --- title: innerHTML use_cases: >- - rendering html strings, dynamic html content, third-party html, legacy content - migration, sanitized markup + rendering HTML strings, replacing element children with parsed markup tags: - html - dom - security - - content - markup version: "1.0" description: >- - Set raw HTML content in SolidJS elements using innerHTML attribute and render - HTML strings dynamically. + Set an element's `innerHTML` property from JSX. --- -The `innerHTML` attribute is equivalent to the [`innerHTML` DOM property](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML). -This attribute replaces all existing nodes of the element with new nodes generated by parsing the provided string as HTML. +`innerHTML` sets an element's `innerHTML` property. -:::caution +## Syntax -Using `innerHTML` with unsanitized user-supplied data can introduce security vulnerabilities. +```tsx +<div innerHTML={html} /> +``` + +## Value + +- **Type:** `string` + +HTML string inserted as raw markup into the element. + +## Behavior + +- Setting `innerHTML` replaces the element's existing children with nodes parsed from the string. +- The value is written through the DOM `innerHTML` property. +- In SSR output, the HTML string is emitted as child content without escaping. +- Unlike [`textContent`](/reference/jsx-attributes/textcontent), `innerHTML` parses markup instead of inserting plain text. +:::caution +Using `innerHTML` with unsanitized user-supplied data can introduce security vulnerabilities. ::: + +## Examples + +### Basic usage + +```tsx +<div innerHTML={"<strong>Hello</strong>"} /> +``` + +## Related + +- [`textContent`](/reference/jsx-attributes/textcontent) diff --git a/src/routes/reference/jsx-attributes/on.mdx b/src/routes/reference/jsx-attributes/on.mdx index 14c47100a..23a55ab7d 100644 --- a/src/routes/reference/jsx-attributes/on.mdx +++ b/src/routes/reference/jsx-attributes/on.mdx @@ -2,51 +2,86 @@ title: "on:*" order: 4 use_cases: >- - custom events, non-bubbling events, capture phase handling, passive listeners, - special event options + direct event listeners, custom event names, event listener options tags: - events - listeners - dom - capture - passive - - handlers version: "1.0" description: >- - Attach non-delegated event handlers with on:* in SolidJS. Control capture, - passive, and once options for advanced event handling requirements. + Attach an event listener directly to an element with `addEventListener`. --- -For events with capital letters, listener options, or if you need to attach event handlers directly to a DOM element instead of optimized delegating via the document, use `on:*` in place of `on*`. +`on:*` attaches an event listener directly to an element with `addEventListener`. + +## Syntax ```tsx -<div on:DOMContentLoaded={(e) => console.log("Welcome!")} /> +<div on:wheel={handler} /> ``` -This directly attaches an event handler (via [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)) to the `div`. +## Value -:::note +- **Type:** event handler function or object with `handleEvent` and optional `AddEventListenerOptions` -<span>New in v1.9.0</span> -::: +Listener passed to `addEventListener`. + +## Behavior -An aditional special syntax that allows full control of [`capture`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#capture), [`passive`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#passive), [`once`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#once) and [`signal`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal) is an intersection or combination of `EventListenerObject` & `AddEventListenerOptions`, as follows: +- `on:name={handler}` attaches a listener for the event named `name`. +- The listener is attached directly to the element instead of using Solid's delegated event system. +- Event names keep the text after `on:` exactly as written. +- Use `on:*` when you need exact event casing, such as case-sensitive custom events. +- When the value changes, Solid removes the previous direct listener and adds the new one. +- Listener options such as `once`, `passive`, `capture`, and `signal` can be provided by passing an object that implements `handleEvent`. +- `oncapture:*` remains available as a deprecated capture-specific form. + +## Examples + +### Basic usage ```tsx +const [message, setMessage] = createSignal("Waiting"); +let el; + +<> + <button + onClick={() => + el.dispatchEvent(new CustomEvent("MyEvent", { detail: "Hello" })) + } + > + Dispatch event + </button> + + <div on:MyEvent={(e) => setMessage(e.detail)} ref={el} /> + <p>{message()}</p> +</>; +``` + +### Listener options + +```tsx +const [count, setCount] = createSignal(0); + const handler = { - handleEvent(e) { - console.log(e) + handleEvent() { + setCount((count) => count + 1); }, - once:true, - passive:false, - capture:true -} + once: true, +}; -<div on:wheel={handler} /> +<> + <button on:click={handler}>Click me</button> + <p>Clicked {count()} time(s)</p> +</>; +``` -// or inline +:::note +The object-listener form was added in Solid 1.9.0. It is useful when you need options such as `once`, `passive`, `capture`, or `signal`. +::: -<div on:click={{passive:true, handleEvent(e) { console.log("Weeeee!")}}} /> -``` +## Related -This new syntax replaces the now deprecated `oncapture:` and it's future proof for any posible new event listener options. +- [`on*`](/reference/jsx-attributes/on_) diff --git a/src/routes/reference/jsx-attributes/on_.mdx b/src/routes/reference/jsx-attributes/on_.mdx index 9f8f314c8..dccfa80ca 100644 --- a/src/routes/reference/jsx-attributes/on_.mdx +++ b/src/routes/reference/jsx-attributes/on_.mdx @@ -2,48 +2,59 @@ title: on* order: 3 use_cases: >- - user interactions, click handlers, form events, keyboard input, mouse events, - touch handling + delegated UI events, click handlers, input handlers, keyboard handlers tags: - events - handlers - - interactions - - click - - input - delegation + - dom version: "1.0" description: >- - Handle user events efficiently in SolidJS with onClick and other event - handlers. Optimized delegation system for improved performance at scale. + Attach an event handler with Solid's delegated event system when the event is + supported. --- -Event handlers in Solid typically take the form of `onclick` or `onClick` depending on style. +`on*` attaches an event handler using Solid's delegated event system when the event is supported. + +## Syntax ```tsx -<div onClick={(e) => console.log(e.currentTarget)} /> +<div onClick={handler} /> ``` -Conceptually, this example attaches a `click` event listener (via [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)) to the `div`. However, Solid actually handles common UI events that bubble and are composed (such as `click`) at the document level, and then synthetically implements delegation (capturing and bubbling). This improves performance for these common events by reducing the number of event handlers. +## Value -Note that `onClick` handles the event `click`; in general, event names get mapped to lower case. If you need to work with event names containing capital letters, or use listener options such once, passive, capture see [`on:`](/reference/jsx-attributes/on) which attaches event handlers directly (also avoiding fancy delegation via document). +- **Type:** event handler function or `[handler, data]` -Solid also supports passing a two-element array to the event handler to bind a value to the first argument of the event handler. This doesn't use `bind` or create an additional closure, so it is a highly optimized way of delegating events. +Event handler, or a handler/data pair for delegated binding. -```tsx -function handler(itemId, e) { - /*...*/ -} +## Behavior + +- Events in Solid's delegated event set use document-level delegation; other `on*` handlers attach directly to the element. +- Event names are mapped to lower case, so `onClick` listens to `click`. +- The two-element array form passes the first item as bound data to the handler. +- Delegated bindings are not reactive and are not rebound automatically when the handler reference changes. +- `onInput` uses the native input event, and `onChange` uses the native change event. +- For direct element listeners, custom event casing, or listener options, use [`on:*`](/reference/jsx-attributes/on). + +## Examples -<ul> - <For each={state.list}>{(item) => <li onClick={[handler, item.id]} />}</For> -</ul>; +### Basic usage + +```tsx +<div onClick={(e) => console.log(e.currentTarget)} /> ``` -Events are never rebound and the bindings are not reactive, as it is expensive to attach and detach listeners. Since event handlers are called like any other function each time an event fires, there is no need for reactivity; shortcut your handler if desired. +### Handler and bound data ```tsx -// if defined, call it; otherwise don't. -<div onClick={() => props.handleClick?.()} /> +function handler(itemId, e) { + console.log(itemId, e); +} + +<For each={state.list}>{(item) => <li onClick={[handler, item.id]} />}</For>; ``` -Note that `onChange` and `onInput` work according to their native behavior (unlike, say, React). [`onInput`](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) will fire immediately after the value has changed; for most `<input>` fields, [`onChange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) will only fire after the field loses focus. The event's `currentTarget` refers to the element that the event was attached to, while `target` gives the element that actually triggered the event (e.g. the user clicked on). +## Related + +- [`on:*`](/reference/jsx-attributes/on) diff --git a/src/routes/reference/jsx-attributes/once.mdx b/src/routes/reference/jsx-attributes/once.mdx index 15d2e71e5..2e110b5d6 100644 --- a/src/routes/reference/jsx-attributes/once.mdx +++ b/src/routes/reference/jsx-attributes/once.mdx @@ -2,29 +2,40 @@ title: "@once" order: 5 use_cases: >- - performance optimization, static props, non-reactive values, compile-time - optimization, reducing overhead + non-reactive JSX expressions, compile-time opt-out from reactive wrapping tags: - - optimization - - performance - - static - compiler - jsx + - optimization version: "1.0" description: >- - Optimize SolidJS components with @once decorator for static values. Prevent - unnecessary reactive wrapping and improve runtime performance. + Mark a JSX expression as static at compile time. --- -Solid's compiler uses a heuristic for reactive wrapping and lazy evaluation of JSX expressions. Does it contain a function call, a property access, or JSX? If yes we wrap it in a getter when passed to components or in an effect if passed to native elements. +`/*@once*/` is a compiler marker comment that marks a JSX expression as static. + +## Syntax + +```tsx +<MyComponent value={/*@once*/ expr} /> +``` + +## Behavior + +- `/*@once*/` applies to the expression that follows it. +- Marked expressions are compiled as static and do not update when reactive dependencies change. +- It can be used in props and in children. +- Some compiler transforms, including parts of `classList` and `style` handling, do not fully respect `/*@once*/`. + +## Examples -Knowing this heuristic and its limitations, we can reduce overhead of things we know will never change by accessing them outside of the JSX. A lone variable will never be wrapped. We can also tell the compiler not to wrap them by starting the expression with a comment decorator `/* @once */`. +### Prop value ```tsx <MyComponent static={/*@once*/ state.wontUpdate} /> ``` -This also works on children. +### Child value ```tsx <MyComponent>{/*@once*/ state.wontUpdate}</MyComponent> diff --git a/src/routes/reference/jsx-attributes/prop.mdx b/src/routes/reference/jsx-attributes/prop.mdx index 8dbdded12..f1265b830 100644 --- a/src/routes/reference/jsx-attributes/prop.mdx +++ b/src/routes/reference/jsx-attributes/prop.mdx @@ -2,27 +2,50 @@ title: "prop:*" order: 6 use_cases: >- - dom properties, scrolltop manipulation, custom properties, property vs - attribute, direct property access + forcing property assignment, writing DOM properties, custom element property + access tags: - properties - dom - attributes - - manipulation - - custom version: "1.0" description: >- - Force DOM property assignment with prop:* in SolidJS. Set properties directly - instead of attributes for specific DOM manipulation needs. + Force a JSX key to be written as a DOM property instead of an attribute. --- -Forces the prop to be treated as a property instead of an attribute. - -```tsx -<div prop:scrollTop={props.scrollPos + "px"} /> -``` +`prop:*` forces a JSX key to be written as a DOM property instead of an attribute. :::note[Strong-Typing Custom Properties] Type definitions are required when using TypeScript. See the [TypeScript](/configuration/typescript#forcing-properties-and-custom-attributes) page for examples. ::: + +## Syntax + +```tsx +<div prop:scrollTop={value} /> +``` + +## Value + +- **Type:** property value + +Value assigned directly to the property after the `prop:` prefix is removed. + +## Behavior + +- On the client, `prop:name={value}` strips the `prop:` prefix and assigns the value directly to the `name` property. +- `prop:*` does not produce SSR output. +- Use `prop:*` when a DOM property must receive the value directly. Solid can still assign some custom-element properties without `prop:*`. + +## Examples + +### Basic usage + +```tsx +<input type="checkbox" prop:indeterminate={true} /> +``` + +## Related + +- [`attr:*`](/reference/jsx-attributes/attr) diff --git a/src/routes/reference/jsx-attributes/ref.mdx b/src/routes/reference/jsx-attributes/ref.mdx index 0ef8edbae..7434dff3b 100644 --- a/src/routes/reference/jsx-attributes/ref.mdx +++ b/src/routes/reference/jsx-attributes/ref.mdx @@ -2,37 +2,56 @@ title: ref order: 7 use_cases: >- - dom access, element manipulation, focus management, measurements, third-party - libraries, animations + element references, imperative DOM access, component refs tags: - refs - dom - elements - - access - - manipulation - - components version: "1.0" description: >- - Access DOM elements directly in SolidJS with refs. Get references to rendered - elements for imperative operations and third-party integrations. + Capture a rendered element or forwarded component ref. --- -Refs are a way of getting access to underlying DOM elements in our JSX. While it is true one could just assign an element to a variable, it is more optimal to leave components in the flow of JSX. Refs are assigned at render time but before the elements are connected to the DOM. They come in 2 flavors. +`ref` captures a rendered element or forwarded component ref. + +## Syntax + +```tsx +<div ref={value} /> +``` + +## Value + +- **Type:** variable binding or callback function + +For DOM elements, variable refs are assigned during render and callback refs receive the element. + +## Behavior + +- Refs are assigned during rendering before the element is connected to the DOM. +- A variable ref assigns the rendered element to the referenced variable. +- A callback ref is called with the rendered element. When `ref` comes through a spread, functional refs are invoked from the spread handling path instead. +- Component refs work only when the component uses or forwards the `ref` prop to an underlying element or child component. + +## Examples + +### Variable ref ```tsx -// variable assigned directly by ref let myDiv; -// use onMount or createEffect to read after connected to the DOM onMount(() => console.log(myDiv)); -<div ref={myDiv} /> +<div ref={myDiv} />; +``` + +### Callback ref -// Or, callback function (called before connected to the DOM) -<div ref={el => console.log(el)} /> +```tsx +<div ref={(el) => console.log(el)} /> ``` -Refs can also be used on Components. They still need to be attached on the other side. +### Component ref ```tsx function MyComp(props) { @@ -45,3 +64,7 @@ function App() { return <MyComp ref={myDiv} />; } ``` + +## Related + +- [`onMount`](/reference/lifecycle/on-mount) diff --git a/src/routes/reference/jsx-attributes/style.mdx b/src/routes/reference/jsx-attributes/style.mdx index 13546e732..d9e8db82e 100644 --- a/src/routes/reference/jsx-attributes/style.mdx +++ b/src/routes/reference/jsx-attributes/style.mdx @@ -2,51 +2,66 @@ title: style order: 7 use_cases: >- - inline styling, dynamic styles, css variables, responsive design, theme - customization, animated styles + inline styles, dynamic style values, CSS variables tags: - styling - css - inline - variables - - dynamic - - properties version: "1.0" description: >- - Apply dynamic inline styles in SolidJS with string or object syntax. Set CSS - properties and variables reactively for responsive component styling. + Set inline styles with a CSS string or an object of property names and values. --- -Solid's style attribute lets you provide either a CSS string or an object where keys are CSS property names: +`style` sets inline styles from either a CSS string or an object. + +## Syntax ```tsx -// string -<div style={`color: green; height: ${state.height}px`} /> +<div style="color: green" /> +<div style={{ color: "green" }} /> +``` -// object -<div style={{ - color: "green", - height: state.height + "px" }} -/> +## Value + +- **Type:** `string | CSSProperties` + +CSS string or style object. + +## Behavior + +- String values are written as inline CSS text. +- Object values are applied property by property with `element.style.setProperty`, so keys should use lower-case, dash-separated CSS property names instead of camelCase. CSS custom properties can use keys such as `--my-color`. +- Nullish values in a style object remove that property, and falsy overall `style` values remove the `style` attribute. +- In SSR output, object values are serialized into the emitted `style` attribute. + +## Examples + +### CSS string + +```tsx +<div style={`color: green; height: ${state.height}px`} /> ``` -Unlike [React's style attribute](https://reactjs.org/docs/dom-elements.html#style), Solid uses **element.style.setProperty** under the hood. This means you need to use the lower-case, dash-separated version of property names instead of the JavaScript camel-cased version, such as `background-color` rather than `backgroundColor`. This actually leads to better performance and consistency with SSR output. +### Style object ```tsx -// string -<div style={`color: green; background-color: ${state.color}; height: ${state.height}px`} /> - -// object -<div style={{ - color: "green", - "background-color": state.color, - height: state.height + "px" }} +<div + style={{ + color: "green", + "background-color": state.color, + height: `${state.height}px`, + }} /> ``` -This also means you can set CSS variables! For example: +### CSS variable ```tsx -// set css variable <div style={{ "--my-custom-color": state.themeColor }} /> ``` + +## Related + +- [`class`](/reference/jsx-attributes/class) +- [`classList`](/reference/jsx-attributes/classlist) diff --git a/src/routes/reference/jsx-attributes/textcontent.mdx b/src/routes/reference/jsx-attributes/textcontent.mdx index b7da41f98..f90639b84 100644 --- a/src/routes/reference/jsx-attributes/textcontent.mdx +++ b/src/routes/reference/jsx-attributes/textcontent.mdx @@ -1,21 +1,43 @@ --- title: textContent use_cases: >- - text-only content, performance optimization, plain text rendering, avoiding - html parsing, simple text updates + text-only content, replacing element children with text tags: - text - - performance - content - - optimization - dom version: "1.0" description: >- - Optimize text rendering with textContent in SolidJS. Bypass diffing for - text-only content and improve performance for simple text updates. + Set an element's `textContent` property from JSX with plain text content. --- -The `textContent` attribute is equivalent to the [`textContent` DOM property](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent). -This attribute replaces all existing child nodes of the element with a single text node containing the provided string. +`textContent` sets an element's `textContent` property with plain text content. -Using `textContent` can improve performance when the element's children are known to be exclusively text, as it bypasses the generic diffing process. +## Syntax + +```tsx +<div textContent={value} /> +``` + +## Value + +- **Type:** `string | number` + +Text written to the element without parsing markup. + +## Behavior + +- `textContent` replaces the element's existing child content instead of merging with JSX children. +- On the client, the value is written through the DOM `textContent` property. During SSR, it is emitted as escaped text instead of raw HTML. + +## Examples + +### Basic usage + +```tsx +<div textContent={"<strong>Hello</strong>"} /> +``` + +## Related + +- [`innerHTML`](/reference/jsx-attributes/innerhtml) diff --git a/src/routes/reference/jsx-attributes/use.mdx b/src/routes/reference/jsx-attributes/use.mdx index 2cf934561..4f7e80cc4 100644 --- a/src/routes/reference/jsx-attributes/use.mdx +++ b/src/routes/reference/jsx-attributes/use.mdx @@ -2,42 +2,57 @@ title: "use:*" order: 5 use_cases: >- - complex dom interactions, tooltips, form handling, two-way data binding, - reusable element behaviors, custom input components + reusable DOM behavior, directives, element setup helpers tags: - directives - dom - - forms - - bindings - - components - - typescript + - jsx version: "1.0" description: >- - Create custom directives in SolidJS to attach reusable behaviors to DOM - elements. Perfect for tooltips, form handling, and two-way data binding. + Attach a directive function to a native element with `use:*`. --- -Custom directives attach reusable behavior to DOM elements, acting as syntactic sugar over `ref`. They’re ideal for complex DOM interactions like scrolling, tooltips, or form handling, which are cumbersome to repeat in JSX. +`use:*` attaches a directive function to a native element. -A directive is a function with the following signature +## Syntax + +```tsx +<input use:model={[value, setValue]} /> +``` + +## Type ```ts -function directive(element: HTMLElement, accessor: Accessor<any>): void; +type Accessor<T> = () => T; + +function directive(element: Element, accessor?: Accessor<any>): void; ``` -Directive functions are called at render time but before being added to the DOM. You can do whatever you'd like in them including create signals, effects, register clean-up etc. +## Value -## Example +- **Type:** directive argument -A `model` directive for two-way data binding +The directive reads the value through an accessor. Without an explicit value, the accessor returns `true`. -```tsx -import type { Accessor, Signal } from "solid-js"; +## Behavior + +- `use:name={value}` passes the element and an accessor for `value` to the directive. +- Without an explicit value, the accessor returns `true`. +- The directive runs during rendering in the current owner, before the element is connected to the DOM, so it can create effects and register cleanup. +- `use:*` works only on native elements, including custom elements, and is not forwarded through user-defined components. + +## Examples -function model(element: HTMLInputElement, value: Accessor<Signal<string>>) { +### Basic usage + +```tsx +function model(element, value) { const [field, setField] = value(); + const onInput = ({ currentTarget }) => setField(currentTarget.value); + createRenderEffect(() => (element.value = field())); - element.addEventListener("input", ({ target }) => setField(target.value)); + element.addEventListener("input", onInput); + onCleanup(() => element.removeEventListener("input", onInput)); } const [name, setName] = createSignal(""); @@ -45,64 +60,17 @@ const [name, setName] = createSignal(""); <input type="text" use:model={[name, setName]} />; ``` -## TypeScript Support - -To type custom directives, extend the `DirectiveFunctions` interface +:::note +When using TypeScript, custom directives may require extending Solid's JSX directive typings: ```ts -declare module "solid-js" { - namespace JSX { - interface DirectiveFunctions { - model: typeof model; - } - } -} -``` - -If you just want to constrain the second argument to the directive function, you can extend the older `Directives` interface - -```tsx declare module "solid-js" { namespace JSX { interface Directives { - model: Signal<string>; + model: [Accessor<string>, Setter<string>]; } } } ``` -## Avoiding Tree-Shaking - -When importing a directive `d` from another module and using it only as `use:d`, TypeScript (via [babel-preset-typescript](https://babeljs.io/docs/babel-preset-typescript)) may remove the import, as it doesn’t recognize `use:d` as a reference to `d`. -To prevent this: - -1. Use the `onlyRemoveTypeImports: true` option in `babel-preset-typescript`. For `vite-plugin-solid`, add this to `vite.config.ts` - - ```ts - import solidPlugin from "vite-plugin-solid"; - - export default { - plugins: [ - solidPlugin({ - typescript: { onlyRemoveTypeImports: true }, - }), - ], - }; - ``` - - Note: This requires consistent use of `export type` and `import type` in your codebase to avoid issues. - -2. Add a fake access like `false && d;` in the module - - ```tsx - import { model } from "./directives"; - false && model; // Prevents tree-shaking - <input type="text" use:model={[name, setName]} />; - ``` - - This is removed by bundlers like Terser, unlike a plain `model;` which may remain in the bundle. - -:::caution[Limitations] -Directives only work with native HTML elements (HTML/SVG/MathML/Custom Elements). -Directives are not forwarded and **won't work in user defined components**, such as `<MyComponent use:myinput={[..]}/>` [see also](https://github.com/solidjs/solid/discussions/722) ::: diff --git a/src/routes/reference/lifecycle/on-cleanup.mdx b/src/routes/reference/lifecycle/on-cleanup.mdx index f2963120e..954a582d5 100644 --- a/src/routes/reference/lifecycle/on-cleanup.mdx +++ b/src/routes/reference/lifecycle/on-cleanup.mdx @@ -12,42 +12,92 @@ 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 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<T extends () => 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 `fn` unchanged. + +## 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. +- Calling `onCleanup` outside a reactive owner does not register a cleanup. In development, Solid warns that the cleanup will never run. +- On the server, cleanup also runs when server-side owners or reactive branches are disposed. + +## Examples + +### Remove an event listener ```tsx -import { createSignal, onCleanup } from "solid-js"; +import { onCleanup } from "solid-js"; const Component = () => { - const [count, setCount] = createSignal(0); - - const handleClick = () => setCount((value) => value + 1); + const handleClick = () => console.log("clicked"); document.addEventListener("click", handleClick); - /** - * Remove the event listener when the component is removed/unmounted from the page. - */ onCleanup(() => { document.removeEventListener("click", handleClick); }); - return <main>Document has been clicked {count()} times</main>; + return <main>Listening for document clicks</main>; }; ``` + +### 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 <button onClick={() => setTopic("sports")}>Change topic</button>; +} +``` + +## 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..2ead251c2 100644 --- a/src/routes/reference/lifecycle/on-mount.mdx +++ b/src/routes/reference/lifecycle/on-mount.mdx @@ -12,32 +12,80 @@ 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. --- -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 + +Non-tracking function executed once on mount. + +## Return value + +`onMount` does not return a value. + +## Behavior + +- On the client, `onMount` runs once after the initial render. It does not run during server rendering. +- `fn` does not track reactive dependencies. +- Internally, `onMount(fn)` is equivalent to `createEffect(() => untrack(fn))`. +- By the time `onMount` runs, refs have already been assigned. +- Returning a function from `fn` does not register cleanup. Use [`onCleanup`](/reference/lifecycle/on-cleanup) inside `onMount` when cleanup is needed. + +## 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 <button ref={ref}>Focus me!</button>; } ``` + +### Run one-time browser setup + +```tsx +import { onMount } from "solid-js"; + +function Example() { + onMount(() => { + // Browser-only code + console.log(window.location.pathname); + }); + + return <div>Mounted</div>; +} +``` + +## 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..ab04ae9bd 100644 --- a/src/routes/reference/reactive-utilities/batch.mdx +++ b/src/routes/reference/reactive-utilities/batch.mdx @@ -11,79 +11,94 @@ 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. + +## Import + ```ts import { batch } from "solid-js"; +``` +## Type + +```ts function batch<T>(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`. -Batching improves performance by avoiding unnecessary recalculation. -Suppose you have a downstream memo `down` that depends on multiple upstream signals `up1`, `up2`, and `up3`: +## Behavior + +- 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, batching applies only to updates before the first `await` or other async suspension point. + +## 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 + +### Basic usage ```ts -import { createSignal, createMemo, createEffect } 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 +const [count, setCount] = createSignal(0); +const [total, setTotal] = createSignal(0); + +const summary = createMemo(() => `${count()} / ${total()}`); +createEffect(() => console.log(summary())); // logs "0 / 0" ``` -If you directly update all of the upstream signals outside of batch mode, then `down` will recompute every time. +Outside `batch`: ```ts -setUp1(4); // recomputes down, outputs 9 -setUp2(5); // recomputes down, outputs 12 -setUp3(6); // recomputes down, outputs 15 +setCount(1); // logs "1 / 0" +setTotal(5); // logs "1 / 5" ``` -If instead you update the upstream signals within a `batch`, then `down` will update only once at the end: +Inside `batch`: ```ts batch(() => { - setUp1(10); // doesn't update down yet - setUp2(10); // doesn't update down yet - setUp3(10); // doesn't update down yet -}); // recomputes down, outputs 30 + setCount(1); + setTotal(5); +}); // logs "1 / 5" ``` -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 inside a batch ```ts batch(() => { - setUp1(11); // doesn't update down yet - setUp2(11); // doesn't update down yet - setUp3(11); // doesn't update down yet - console.log(down()); // recomputes down, outputs 33 - setUp1(12); // doesn't update down yet - setUp2(12); // doesn't update down yet - setUp3(12); // doesn't update down yet -}); // recomputes down, outputs 36 + setCount(2); + console.log(summary()); // logs "2 / 5" + setTotal(10); +}); // logs "2 / 10" ``` -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/catch-error.mdx b/src/routes/reference/reactive-utilities/catch-error.mdx index 6d5363792..b9b5b4c77 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. -<span>New in v1.7.0</span> -::: +## Import -```tsx +```ts import { catchError } from "solid-js"; +``` + +## Type + +```ts +function catchError<T>( + 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 a thrown value reaches this boundary. -function catchError<T>(tryFn: () => T, onError: (err: any) => void): T; +## Return value + +- **Type:** `T | undefined` + +Returns the value produced by `fn`, or `undefined` when a thrown value is caught and handled. + +## Behavior + +- While `fn` runs, `catchError` establishes an error boundary for reactive scopes created inside it. +- 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 <button onClick={() => setCount((c) => c + 1)}>Increment</button>; +} ``` -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/from.mdx b/src/routes/reference/reactive-utilities/from.mdx index 1fe6be429..07a28c0c6 100644 --- a/src/routes/reference/reactive-utilities/from.mdx +++ b/src/routes/reference/reactive-utilities/from.mdx @@ -11,45 +11,107 @@ 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<T>( - producer: - | ((setter: (v: T) => T) => () => void) - | { - subscribe: ( - fn: (v: T) => void - ) => (() => void) | { unsubscribe: () => void }; - } -): () => T | undefined; +## Type + +```ts +function from<T>(producer: Producer<T>, initialValue: T): () => T; +function from<T>(producer: Producer<T | undefined>): () => T | undefined; + +type Producer<T> = + | ((setter: Setter<T>) => () => 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<T>` or `Producer<T | undefined>` +- **Required:** Yes + +Producer function or subscribable object. + +### `initialValue` + +- **Type:** `T` +- **Required:** No + +Initial value for the accessor. + +## Return value + +- **Type:** `() => T` or `() => T | undefined` + +Returns an accessor backed by the external source. + +## Behavior + +- `from` subscribes to the producer immediately when it runs. +- A producer function receives a Solid setter and returns a cleanup function. +- A subscribable source must expose `subscribe()`, and that subscription may return either a cleanup function or an object with `unsubscribe()`. +- Emitted values are not deduplicated by equality. +- Cleanup is registered on the current Solid owner when one exists. +- When `from` runs without an owner, the subscription still starts but is not cleaned up automatically. +- When `initialValue` is omitted, the accessor can return `undefined`. + +## Examples + +### Create an accessor from a subscribable source ```tsx -const signal = from(obsv$); +import { from } from "solid-js"; + +function Clock() { + const time = from( + { + subscribe(next) { + const interval = setInterval( + () => next(new Date().toLocaleTimeString()), + 1000 + ); + return () => clearInterval(interval); + }, + }, + "" + ); + + return <div>{time()}</div>; +} ``` -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 ```tsx -const clock = from((set) => { - const interval = setInterval(() => { - set((v) => v + 1); - }, 1000); +import { from } from "solid-js"; + +function Counter() { + const count = from((set) => { + const interval = setInterval(() => { + set((value = 0) => value + 1); + }, 1000); + + return () => clearInterval(interval); + }, 0); - return () => clearInterval(interval); -}); + return <div>{count()}</div>; +} ``` -## 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/get-owner.mdx b/src/routes/reference/reactive-utilities/get-owner.mdx index 0825246ca..506b962d6 100644 --- a/src/routes/reference/reactive-utilities/get-owner.mdx +++ b/src/routes/reference/reactive-utilities/get-owner.mdx @@ -11,26 +11,70 @@ 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 +type Owner = unknown; + +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. +The `Owner` interface is public, but its concrete shape is mostly useful for advanced interop such as [`runWithOwner`](/reference/reactive-utilities/run-with-owner). + +## Parameters + +`getOwner` does not take any parameters. + +## Return value + +- **Type:** `Owner | null` -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. +Returns the current owner or `null` when no owner is active. + +## Behavior + +- `getOwner` returns the current owner and does not create or modify ownership. +- 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. +- Component functions run under an owner created for that subtree. +- 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 + +```tsx +import { getOwner, runWithOwner } from "solid-js"; + +function Example() { + const owner = getOwner(); + + queueMicrotask(() => { + if (owner) { + runWithOwner(owner, () => { + console.log("owner restored"); + }); + } + }); + + return null; +} +``` -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 (`<Component ...>`). +- [`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/index-array.mdx b/src/routes/reference/reactive-utilities/index-array.mdx index 421b836a8..43a0820f5 100644 --- a/src/routes/reference/reactive-utilities/index-array.mdx +++ b/src/routes/reference/reactive-utilities/index-array.mdx @@ -11,43 +11,84 @@ 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<T, U>( - 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 `<Index>` 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` + +#### `fallback` + +- **Type:** `() => any` + +Fallback accessor used when the source array is empty or falsy. +The mapped result becomes a single fallback entry. + +## 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. +- Reordering the source array changes which item each index points to instead of moving mapped entries by identity. +- This is the underlying helper for [`<Index>`](/reference/components/index-component). + +## Examples + +### Map an array by index + +```ts +import { createSignal, indexArray } from "solid-js"; + +const [source] = createSignal([{ status: "pending" }, { status: "done" }]); + +const mapped = indexArray(source, (item, index) => ({ + index, + status: () => item().status, +})); ``` -## Arguments +## Related -| Name | Type | Description | -| :---- | :----------------------------- | :-------------------- | -| list | `() => readonly T[]` | The list to map. | -| mapFn | `(v: () => T, i: number) => U` | The mapping function. | +- [`<Index>`](/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..71014379a 100644 --- a/src/routes/reference/reactive-utilities/map-array.mdx +++ b/src/routes/reference/reactive-utilities/map-array.mdx @@ -11,47 +11,86 @@ 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<T, U>( - 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` -Underlying helper for the `<For>` control flow. +#### `fallback` + +- **Type:** `() => any` + +Fallback accessor used when the source array is empty or falsy. The mapped result becomes a single fallback entry. + +## 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, so reads inside the callback do not track unless they happen inside nested JSX or another reactive scope. +- Reordering reuses existing mapped items for retained source values and updates their index accessors. +- This is the underlying helper for [`<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, status: "pending" }, + { id: 2, status: "done" }, +]); + +const mapped = mapArray(source, (item, index) => ({ + id: item.id, + status: item.status, + position: () => index(), +})); ``` -## Arguments +## Related -| Name | Type | Description | -| :---- | :----------------------------- | :----------------------- | -| list | `() => readonly T[]` | The source array to map. | -| mapFn | `(v: T, i: () => number) => U` | The mapping function. | +- [`<For>`](/reference/components/for) +- [`indexArray`](/reference/reactive-utilities/index-array) diff --git a/src/routes/reference/reactive-utilities/merge-props.mdx b/src/routes/reference/reactive-utilities/merge-props.mdx index 85eab1176..26099a905 100644 --- a/src/routes/reference/reactive-utilities/merge-props.mdx +++ b/src/routes/reference/reactive-utilities/merge-props.mdx @@ -1,41 +1,74 @@ --- 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<T extends unknown[]>(...sources: T): MergeProps<T>; ``` -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 -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. +### `sources` -```ts -// default props -props = mergeProps({ name: "Smith" }, props); +- **Type:** `unknown[]` + +Prop sources to merge. + +## Return value + +- **Type:** `MergeProps<T>` -// clone props -newProps = mergeProps(props); +Returns a merged object with lazy reactive property resolution across the provided sources. -// merge props -props = mergeProps(props, otherProps); +## Behavior + +- `mergeProps` is shallow. +- The last source with a non-`undefined` value for a property wins. +- 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 + +```tsx +import { mergeProps } from "solid-js"; + +function Greeting(props) { + const merged = mergeProps({ greeting: "Hello", name: "Smith" }, props); + + return ( + <div> + {merged.greeting} {merged.name} + </div> + ); +} ``` + +## Related + +- [`splitProps`](/reference/reactive-utilities/split-props) diff --git a/src/routes/reference/reactive-utilities/observable.mdx b/src/routes/reference/reactive-utilities/observable.mdx index e879bae13..e1203784d 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<T>(input: () => T): Observable<T>; ``` -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<T>` + +Returns an object with `subscribe()` and `[Symbol.observable]()` that returns the same object. + +## Behavior + +- `subscribe()` accepts either a function observer or an object observer with `next`. +- Each subscription creates an effect over the accessor and returns an object with `unsubscribe()`. +- If subscription happens inside an owned Solid scope, cleanup is also registered on that owner. + +## 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) diff --git a/src/routes/reference/reactive-utilities/run-with-owner.mdx b/src/routes/reference/reactive-utilities/run-with-owner.mdx index 349243cda..9d52709a3 100644 --- a/src/routes/reference/reactive-utilities/run-with-owner.mdx +++ b/src/routes/reference/reactive-utilities/run-with-owner.mdx @@ -13,45 +13,78 @@ 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"; +``` + +## Type + ```ts -import { runWithOwner } from "solid-js" -import type { Owner } from "solid-js" +type Owner = unknown; -function runWithOwner<T>(owner: Owner, fn: (() => void) => T): T +function runWithOwner<T>(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 -Having a (correct) owner is important for two reasons: +### `owner` -- 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. +- **Type:** `Owner | null` +- **Required:** Yes -- `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). +Owner used while executing `fn`. -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: +### `fn` -```ts -const owner = getOwner(); -setTimeout(() => { - // This callback gets run without owner. - // Restore owner via runWithOwner: - runWithOwner(owner, () => { - const foo = useContext(FooContext); - createEffect(() => { - console.log(foo); - }); +- **Type:** `() => T` +- **Required:** Yes + +Function executed under `owner`. + +## Return value + +- **Type:** `T | undefined` + +Returns the value produced by `fn`, or `undefined` when an error is routed through Solid's error handling. + +## Behavior + +- During the synchronous execution of `fn`, `runWithOwner` restores the provided owner for cleanup, context lookup, and descendant computations created inside `fn`. +- `runWithOwner` does not restore dependency tracking because the current tracking listener is cleared while `fn` runs. +- Code after the first `await` in an `async` function runs without the restored owner or reactive dependency tracking. + +## Examples + +### Restore an owner in a callback + +```tsx +import { createEffect, getOwner, runWithOwner } from "solid-js"; + +function Example() { + const owner = getOwner(); + + queueMicrotask(() => { + if (owner) { + runWithOwner(owner, () => { + createEffect(() => { + console.log("effect created under the captured owner"); + }); + }); + } }); -}, 1000); + + return null; +} ``` -**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) +- [`onCleanup`](/reference/lifecycle/on-cleanup) diff --git a/src/routes/reference/reactive-utilities/split-props.mdx b/src/routes/reference/reactive-utilities/split-props.mdx index 0973baf63..a6b8d4496 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,62 @@ 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<T>( - props: T, - ...keys: Array<(keyof T)[]> -): [...parts: Partial<T>]; +```ts +function splitProps< + T extends Record<any, any>, + K extends [readonly (keyof T)[], ...(readonly (keyof T)[])[]], +>(props: T, ...keys: K): SplitProps<T, K>; ``` -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:** `SplitProps<T, typeof keys>` -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 a tuple of reactive subsets followed by a reactive remainder object. -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. +- A key is assigned to the first matching group only. +- 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. +- `splitProps` separates props into groups without destructuring them into non-reactive locals. + +## Examples + +### Basic usage ```tsx +import { splitProps } from "solid-js"; + function MyComponent(props) { const [local, others] = splitProps(props, ["children"]); @@ -44,23 +79,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 -<MyComponent a={1} b={2} c={3} d={4} e={5} foo="bar" />; -// ... +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 ( + <Child vowels={vowels} consonants={consonants} leftovers={leftovers} /> + ); } ``` + +## Related + +- [`mergeProps`](/reference/reactive-utilities/merge-props) diff --git a/src/routes/reference/reactive-utilities/start-transition.mdx b/src/routes/reference/reactive-utilities/start-transition.mdx index b628412ea..1aa7e1c1d 100644 --- a/src/routes/reference/reactive-utilities/start-transition.mdx +++ b/src/routes/reference/reactive-utilities/start-transition.mdx @@ -11,16 +11,83 @@ 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<void> +## Type +```ts +function startTransition(fn: () => unknown): Promise<void>; ``` -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<void>` + +Resolves when the transition completes. + +## Behavior + +- On the client, `fn` runs asynchronously in a microtask and its updates run as a transition. +- Nested calls inside an active transition reuse that transition and return its existing promise, which resolves after transition work completes. +- On the server, `startTransition(fn)` runs `fn()` synchronously. +- `startTransition` is the transition-starting function exposed by [`useTransition`](/reference/reactive-utilities/use-transition), without the `pending` accessor. + +## Examples + +### Basic usage + +```tsx +import { + Suspense, + createResource, + createSignal, + startTransition, +} from "solid-js"; + +function Example() { + const [userId, setUserId] = createSignal(1); + const [user] = createResource(userId, async (id) => { + const response = await fetch(`/api/users/${id}`); + return response.json(); + }); + + async function showNextUser() { + await startTransition(() => { + setUserId(2); + }); + } + + return ( + <> + <button onClick={showNextUser}>Load next user</button> + <Suspense fallback={<p>Loading user...</p>}> + <pre>{JSON.stringify(user(), null, 2)}</pre> + </Suspense> + </> + ); +} +``` + +## Related + +- [`useTransition`](/reference/reactive-utilities/use-transition) +- [`Suspense`](/reference/components/suspense) diff --git a/src/routes/reference/reactive-utilities/untrack.mdx b/src/routes/reference/reactive-utilities/untrack.mdx index 2b38a9383..4c9cfbbe0 100644 --- a/src/routes/reference/reactive-utilities/untrack.mdx +++ b/src/routes/reference/reactive-utilities/untrack.mdx @@ -12,28 +12,88 @@ 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. --- -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<T>(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` unchanged. + +## Behavior + +- `untrack` only affects reads inside the provided function. Signals read there do not become dependencies of the surrounding computation. +- `untrack` does not create or restore an owner. + +## Examples + +### Read part of an effect without tracking + +```tsx +import { createEffect, untrack } from "solid-js"; export function Component(props) { - const value = untrack(() => props.value); + createEffect(() => { + console.log( + props.id, + untrack(() => props.label) + ); + }); - return <div>{value}</div>; + return <div>{props.id}</div>; } ``` -## 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 <button onClick={() => setCount((c) => c + 1)}>Increment</button>; +} +``` + +## 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 +104,6 @@ export function Component(props) { ``` ```tsx tab title="defaultValue" {5} -// component.tsx import { createSignal } from "solid-js"; export function Component(props) { @@ -53,3 +112,9 @@ export function Component(props) { return <div>{name()}</div>; } ``` + +## Related + +- [`onMount`](/reference/lifecycle/on-mount) +- [`createEffect`](/reference/basic-reactivity/create-effect) +- [`on`](/reference/reactive-utilities/on-util) diff --git a/src/routes/reference/reactive-utilities/use-transition.mdx b/src/routes/reference/reactive-utilities/use-transition.mdx index 71ae753d8..c43d10842 100644 --- a/src/routes/reference/reactive-utilities/use-transition.mdx +++ b/src/routes/reference/reactive-utilities/use-transition.mdx @@ -12,28 +12,86 @@ 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<void>, + start: (fn: () => void) => Promise<void>, ]; ``` -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<void>]` + +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` is the same transition-starting function exposed by [`startTransition`](/reference/reactive-utilities/start-transition). +- On the client, `start` schedules its callback asynchronously in a microtask. +- `pending()` reflects whether that transition is still pending. +- Transition state integrates with Suspense and resource reads under Suspense boundaries. +- On the server, `pending()` is `false` and transitions run synchronously. + +## Examples + +### Basic usage + +```tsx +import { + Suspense, + createResource, + createSignal, + useTransition, +} from "solid-js"; + +function Example() { + const [userId, setUserId] = createSignal(1); + const [user] = createResource(userId, async (id) => { + const response = await fetch(`/api/users/${id}`); + return response.json(); + }); + const [pending, start] = useTransition(); + + return ( + <> + <button + onClick={async () => { + await start(() => setUserId(2)); + }} + > + Load next user + </button> + <div>{pending() ? "Loading transition..." : "Ready"}</div> + <Suspense fallback={<p>Loading user...</p>}> + <pre>{JSON.stringify(user(), null, 2)}</pre> + </Suspense> + </> + ); +} ``` + +## Related + +- [`startTransition`](/reference/reactive-utilities/start-transition) +- [`Suspense`](/reference/components/suspense) 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..f019ecf69 100644 --- a/src/routes/reference/rendering/dev.mdx +++ b/src/routes/reference/rendering/dev.mdx @@ -1,37 +1,66 @@ --- 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`. +It exposes Solid's development hooks and internals for development tooling and diagnostics. + +## 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 the development browser bundle, `DEV` is defined and exposes development hooks and internals. +- In production and server bundles, `DEV` is `undefined`. +- `DEV` is intended for tooling, diagnostics, and library code that needs development-only behavior. + +## 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.warn("development-only check"); } ``` + +## 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..0a57d5858 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,59 @@ 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 -```ts -const dispose = hydrate(App, document.getElementById("app")); +### `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` + +Scopes hydration lookup to a specific rendered subtree. + +#### `owner` + +- **Type:** `unknown` + +Owner used for the created hydration root. + +## Return value + +- **Type:** `() => void` + +Dispose function for the hydrated root. + +## Behavior + +- `hydrate` is a client-only API. +- `hydrate` reuses DOM produced by Solid's server renderer instead of creating a new subtree, locating nodes through Solid's hydration markers and optionally scoping that lookup with `renderId`. +- The hydrated DOM and the JSX returned by `fn` must match the server output for hydration to succeed. +- The returned function disposes the hydrated root. + +## Examples + +### Basic usage + +```tsx +import { hydrate } from "solid-js/web"; + +// #app already contains server-rendered Solid markup +const dispose = hydrate(() => <App />, 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..33225c728 100644 --- a/src/routes/reference/rendering/hydration-script.mdx +++ b/src/routes/reference/rendering/hydration-script.mdx @@ -1,38 +1,109 @@ --- 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. + Generate the SSR hydration bootstrap script with `HydrationScript` or + `generateHydrationScript`. --- +`HydrationScript` and `generateHydrationScript` generate Solid's SSR 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 `<script>` tag. + +### `eventNames` + +- **Type:** `string[]` + +Delegated event names captured before client scripts load. + +## `generateHydrationScript` options + +### `nonce` + +- **Type:** `string` + +Nonce applied to the generated script tag. + +### `eventNames` + +- **Type:** `string[]` + +Delegated event names captured before client scripts load. + +## Return value + +- **Type:** `JSX.Element` for `HydrationScript`, `string` for `generateHydrationScript` + +## Behavior + +- The generated script initializes `window._$HY` and bootstraps delegated event replay before the runtime loads. +- The default captured delegated events are `"click"` and `"input"` unless `eventNames` is overridden. +- Place the generated script once in the server-rendered document when the page will hydrate on the client. +- `HydrationScript` returns JSX for server-rendered HTML output, and `generateHydrationScript` returns a string for manual HTML generation. +- In browser bundles, these exports are placeholders that return `undefined`. + +## Examples + +### `HydrationScript` + +```tsx +import { HydrationScript } from "solid-js/web"; +import type { JSX } from "solid-js"; + +function Html(props: { children: JSX.Element }) { + return ( + <html lang="en"> + <head> + <HydrationScript /> + </head> + <body>{props.children}</body> + </html> + ); +} +``` + +### `generateHydrationScript` + +```ts +import { generateHydrationScript } from "solid-js/web"; + +const script = generateHydrationScript({ nonce: "nonce-value" }); ``` -Hydration Script is a special script that should be placed once on the page to bootstrap hydration before Solid's runtime has loaded. -It comes both as a function that can be called and inserted in an HTML string, or as a Component if you are rendering JSX from the `<html>` tag. +## Related -The options are for the **nonce** to be put on the script tag and any event names for that Solid should capture before scripts have loaded and replay during hydration. -These events are limited to those that Solid delegates which include most UI Events that are composed and bubble. -By default it is only click and input events. +- [`hydrate`](/reference/rendering/hydrate) diff --git a/src/routes/reference/rendering/is-dev.mdx b/src/routes/reference/rendering/is-dev.mdx new file mode 100644 index 000000000..7ab4a7c20 --- /dev/null +++ b/src/routes/reference/rendering/is-dev.mdx @@ -0,0 +1,50 @@ +--- +title: isDev +use_cases: >- + development-build checks, browser-side dev-only branches, bundle-level + development flags +tags: + - development + - debugging + - builds + - conditional +version: "1.0" +description: >- + Read the boolean that indicates whether the imported `solid-js/web` bundle is the development browser bundle. +--- + +`isDev` is a constant boolean exported by `solid-js/web`. + +## Import + +```ts +import { isDev } from "solid-js/web"; +``` + +## Type + +```ts +const isDev: boolean; +``` + +## Behavior + +- `isDev` is a bundle constant: it is `true` in the development browser bundle and `false` in production and server bundles. [`DEV`](/reference/rendering/dev) is a separate development-only export from `solid-js`. +- Because it is exported as a constant, bundlers can eliminate unreachable branches. + +## Examples + +### Basic usage + +```ts +import { isDev } from "solid-js/web"; + +if (isDev) { + debugPanel.mount(); +} +``` + +## Related + +- [`DEV`](/reference/rendering/dev) +- [`isServer`](/reference/rendering/is-server) diff --git a/src/routes/reference/rendering/is-server.mdx b/src/routes/reference/rendering/is-server.mdx index ee24a1bd5..331f40f54 100644 --- a/src/routes/reference/rendering/is-server.mdx +++ b/src/routes/reference/rendering/is-server.mdx @@ -1,35 +1,49 @@ --- title: isServer use_cases: >- - conditional rendering, ssr vs client code, bundle optimization, - environment-specific logic, code splitting + server-only branches, client-only branches, bundle-level environment checks tags: - ssr - environment - bundling - - optimization - conditional version: "1.0" description: >- - Detect server or browser environment with isServer. Optimize bundle sizes by - conditionally including code for specific runtime environments. + Read whether the current `solid-js/web` bundle is the server bundle. --- +`isServer` is a constant boolean that indicates whether code is running in the server bundle. + +## Import + ```ts import { isServer } from "solid-js/web"; +``` +## Type + +```ts const isServer: boolean; ``` -This indicates that the code is being run as the server or browser bundle. -As the underlying runtimes export this as a constant boolean it allows bundlers to eliminate the code and their used imports from the respective bundles. +## Behavior + +- `isServer` is `true` in the server bundle and `false` in the browser bundle. +- Because it is exported as a constant, bundlers can eliminate unreachable branches. + +## Examples + +### Basic usage ```ts import { isServer } from "solid-js/web"; if (isServer) { - // I will never make it to the browser bundle -} else { - // won't be run on the server; + serverOnlyWork(); } ``` + +## Related + +- [`DEV`](/reference/rendering/dev) +- [`isDev`](/reference/rendering/is-dev) diff --git a/src/routes/reference/rendering/render-to-stream.mdx b/src/routes/reference/rendering/render-to-stream.mdx index 5eda94cf0..7ea469481 100644 --- a/src/routes/reference/rendering/render-to-stream.mdx +++ b/src/routes/reference/rendering/render-to-stream.mdx @@ -1,30 +1,37 @@ --- title: renderToStream use_cases: >- - streaming ssr, progressive rendering, async data loading, suspense boundaries, - large page optimization + streaming server rendering, progressive HTML delivery, suspense-driven server + output tags: - ssr - streaming - async - suspense - - performance version: "1.0" description: >- - Stream HTML progressively with renderToStream. Render content synchronously - then stream async resources as they complete for faster loading. + Stream server-rendered HTML and continue writing async content as it + resolves. --- +`renderToStream` streams server-rendered HTML and continues writing async content as it resolves. + +## Import + ```ts import { renderToStream } from "solid-js/web"; +``` +## Type + +```ts function renderToStream<T>( fn: () => T, options?: { nonce?: string; renderId?: string; - onCompleteShell?: () => void; - onCompleteAll?: () => void; + onCompleteShell?: (info: { write: (v: string) => void }) => void; + onCompleteAll?: (info: { write: (v: string) => void }) => void; } ): { pipe: (writable: { write: (v: string) => void }) => void; @@ -32,32 +39,74 @@ function renderToStream<T>( }; ``` -This method renders to a stream. -It renders the content synchronously including any Suspense fallback placeholders, and then continues to stream the data and HTML from any async resource as it completes. +## Parameters -```ts -// node -renderToStream(App).pipe(res); +### `fn` + +- **Type:** `() => T` + +Function that returns the root output to render. + +### `options` + +#### `nonce` + +- **Type:** `string` + +Nonce applied to inline scripts emitted during rendering. + +#### `renderId` + +- **Type:** `string` + +Identifier used to namespace the render output. + +#### `onCompleteShell` + +- **Type:** `(info: { write: (v: string) => void }) => void` -// web stream -const { readable, writable } = new TransformStream(); -renderToStream(App).pipeTo(writable); +Callback invoked when the shell is ready to flush. + +#### `onCompleteAll` + +- **Type:** `(info: { write: (v: string) => void }) => void` + +Callback invoked after all server suspense boundaries have settled. + +## Return value + +- **Type:** `{ pipe: ..., pipeTo: ... }` + +Streaming controller with `pipe` and `pipeTo` methods. + +## Behavior + +- `renderToStream` is a server rendering API and is unsupported in browser bundles. +- It renders the shell first, including suspense fallback content, and can flush that output before later async fragments and serialized data stream as resources resolve. +- `onCompleteShell` and `onCompleteAll` receive a `write()` helper for injecting additional output into the stream. +- `pipe` writes to Node-style writable targets. +- `pipeTo` writes to a `WritableStream`. + +## Examples + +### `pipe` + +```tsx +import { renderToStream } from "solid-js/web"; + +renderToStream(() => <App />).pipe(response); ``` -`onCompleteShell` fires when synchronous rendering is complete before writing the first flush to the stream out to the browser. -`onCompleteAll` is called when all server Suspense boundaries have settled. -`renderId` is used to namespace renders when having multiple top level roots. +### `pipeTo` + +```tsx +import { renderToStream } from "solid-js/web"; -:::note -This API replaces the previous pipeToWritable and pipeToNodeWritable -APIs. -::: +const { writable } = new TransformStream(); +renderToStream(() => <App />).pipeTo(writable); +``` -## Options +## Related -| Name | Type | Description | -| --------------- | ---------- | ---------------------------------------------------------------- | -| nonce | string | The nonce to use for inline scripts. | -| renderId | string | The id to use for this render. | -| onCompleteShell | () => void | A callback that fires when the shell is complete. | -| onCompleteAll | () => void | A callback that fires when all Suspense boundaries have settled. | +- [`renderToString`](/reference/rendering/render-to-string) +- [`renderToStringAsync`](/reference/rendering/render-to-string-async) diff --git a/src/routes/reference/rendering/render-to-string-async.mdx b/src/routes/reference/rendering/render-to-string-async.mdx index f5ebcd4f9..b6fcf3ad1 100644 --- a/src/routes/reference/rendering/render-to-string-async.mdx +++ b/src/routes/reference/rendering/render-to-string-async.mdx @@ -1,46 +1,92 @@ --- title: renderToStringAsync use_cases: >- - ssr with async data, suspense resolution, complete page rendering, data - serialization, seo optimization + async server rendering, waiting for suspense boundaries, HTML generation with + async data tags: - ssr - async - suspense - - serialization - rendering version: "1.0" description: >- - Render complete HTML with async data using renderToStringAsync. Waits for all - Suspense boundaries before returning serialized results. + Render HTML to a string after async suspense boundaries settle. --- +`renderToStringAsync` renders HTML to a string after async suspense boundaries settle. + +## Import + ```ts import { renderToStringAsync } from "solid-js/web"; +``` + +## Type +```ts function renderToStringAsync<T>( fn: () => T, options?: { timeoutMs?: number; - renderId?: string; nonce?: string; + renderId?: string; } ): Promise<string>; ``` -Same as `renderToString` except that it will wait for all `<Suspense>` boundaries to resolve before returning the results. -Resource data is automatically serialized into the script tag and will be hydrated on client load. +## Parameters -`renderId` is used to namespace renders when having multiple top level roots. +### `fn` -```ts -const html = await renderToStringAsync(App); +- **Type:** `() => T` + +Function that returns the root output to render. + +### `options` + +#### `timeoutMs` + +- **Type:** `number` + +Maximum wait time before the returned promise rejects. + +#### `nonce` + +- **Type:** `string` + +Nonce applied to inline scripts emitted during rendering. + +#### `renderId` + +- **Type:** `string` + +Identifier used to namespace the render output. + +## Return value + +- **Type:** `Promise<string>` + +Promise that resolves to the rendered HTML string. + +## Behavior + +- `renderToStringAsync` is a server rendering API and is unsupported in browser bundles. +- It waits for server suspense boundaries to settle before resolving the final HTML string. +- `timeoutMs` limits how long the render waits for async suspense work to finish. +- Resource data is serialized for client hydration. +- `renderId` namespaces the render output when multiple top-level roots are present. + +## Examples + +### Basic usage + +```tsx +import { renderToStringAsync } from "solid-js/web"; + +const html = await renderToStringAsync(() => <App />); ``` -## Options +## Related -| Name | Type | Description | -| ----------- | -------- | -------------------------------------------------------------------------------------------- | -| `timeoutMs` | `number` | The number of milliseconds to wait for a `<Suspense>` boundary to resolve before timing out. | -| `renderId` | `string` | The id to use for the render. | -| `nonce` | `string` | The nonce to use for the script tag. | +- [`renderToString`](/reference/rendering/render-to-string) +- [`renderToStream`](/reference/rendering/render-to-stream) diff --git a/src/routes/reference/rendering/render-to-string.mdx b/src/routes/reference/rendering/render-to-string.mdx index 74dd17262..3ce5667ca 100644 --- a/src/routes/reference/rendering/render-to-string.mdx +++ b/src/routes/reference/rendering/render-to-string.mdx @@ -1,23 +1,28 @@ --- title: renderToString use_cases: >- - basic ssr, static site generation, synchronous rendering, progressive - hydration, seo pages + synchronous server rendering, HTML generation, progressive hydration output tags: - ssr - rendering - hydration - synchronous - - static version: "1.0" description: >- - Generate HTML strings synchronously with renderToString. Perfect for SSR with - progressive hydration and static site generation needs. + Render HTML to a string synchronously on the server. --- +`renderToString` renders HTML to a string synchronously on the server. + +## Import + ```ts import { renderToString } from "solid-js/web"; +``` +## Type + +```ts function renderToString<T>( fn: () => T, options?: { @@ -27,19 +32,53 @@ function renderToString<T>( ): string; ``` -Renders to a string synchronously. -The function also generates a script tag for progressive hydration. -Options include eventNames to listen to before the page loads and play back on hydration, and nonce to put on the script tag. +## Parameters -`renderId` is used to namespace renders when having multiple top level roots. +### `fn` -```ts -const html = renderToString(App); +- **Type:** `() => T` + +Function that returns the root output to render. + +### `options` + +#### `nonce` + +- **Type:** `string` + +Nonce applied to inline scripts emitted during rendering. + +#### `renderId` + +- **Type:** `string` + +Identifier used to namespace the render output. + +## Return value + +- **Type:** `string` + +Rendered HTML string. + +## Behavior + +- `renderToString` is a server rendering API, is unsupported in browser bundles, and completes synchronously. +- It returns the current render output without waiting for async suspense boundaries to settle. +- The output includes hydration markup. Inline serialized scripts are emitted only when serializer data is produced. +- Registered assets are injected into the HTML output, typically before `</head>`. +- `renderId` namespaces the render output when multiple top-level roots are present. + +## Examples + +### Basic usage + +```tsx +import { renderToString } from "solid-js/web"; + +const html = renderToString(() => <App />); ``` -## Options +## Related -| Name | Type | Description | -| ---------- | -------- | ------------------------------------ | -| `nonce` | `string` | The nonce to use for the script tag. | -| `renderId` | `string` | The id to use for the script tag. | +- [`renderToStringAsync`](/reference/rendering/render-to-string-async) +- [`renderToStream`](/reference/rendering/render-to-stream) diff --git a/src/routes/reference/rendering/render.mdx b/src/routes/reference/rendering/render.mdx index 0d0aaac86..b78314e51 100644 --- a/src/routes/reference/rendering/render.mdx +++ b/src/routes/reference/rendering/render.mdx @@ -1,44 +1,74 @@ --- title: render use_cases: >- - app initialization, mounting components, spa entry point, client-side - rendering, dom manipulation + mounting a client app, browser entry points, attaching a root to the DOM tags: - mounting - - initialization - - spa - - dom + - rendering - client - - entry + - dom version: "1.0" description: >- - Mount your Solid app to the DOM with render. The essential browser entry point - for initializing and disposing client-side applications. + Mount a Solid root into a DOM node in the browser. --- +`render` mounts a Solid root into a DOM node in the browser. + +## Import + ```ts import { render } from "solid-js/web"; -import type { JSX } from "solid-js"; -import type { MountableElement } from "solid-js/web"; - -function render(code: () => JSX.Element, element: MountableElement): () => void; ``` -This is the browser app entry point. -Provide a top-level component function and an element to mount to. -It is recommended this element be empty: while `render` will just append children, the returned dispose function will remove all children. +## Type ```ts -const dispose = render(App, document.getElementById("app")) -// or -const dispose = render(() => <App />, document.getElementById("app")) -``` +type MountableElement = + | Element + | Document + | ShadowRoot + | DocumentFragment + | Node; -It's important that the first argument is a function: do not pass JSX directly (as in `render(<App/>, ...)`), because this will call App before render can set up a root to track signal dependencies within App. +function render(code: () => JSX.Element, element: MountableElement): () => void; +``` ## Parameters -| Argument | Type | Description | -| -------- | ------------------- | ------------------------------------------- | -| code | `() => JSX.Element` | Function that returns the application code. | -| element | MountableElement | DOM Element to mount the application to | +### `code` + +- **Type:** `() => JSX.Element` + +Function that returns the root JSX to mount. + +### `element` + +- **Type:** `MountableElement` + +DOM node that receives the rendered output. + +## Return value + +- **Type:** `() => void` + +Dispose function for the mounted root. + +## Behavior + +- `render` is a browser/client API and is unsupported in the server bundle. It creates a new root and appends its output to `element`. When `element` is `document`, it evaluates `code()` directly instead of using DOM insertion. +- The first argument must be a function so Solid can establish the root before evaluating JSX. +- The returned function disposes the root and clears the mount container's content, so mounting into an empty container is safest. + +## Examples + +### Basic usage + +```tsx +import { render } from "solid-js/web"; + +const dispose = render(() => <App />, document.getElementById("app")!); +``` + +## Related + +- [`hydrate`](/reference/rendering/hydrate) diff --git a/src/routes/reference/secondary-primitives/create-computed.mdx b/src/routes/reference/secondary-primitives/create-computed.mdx index 84da443c4..624067c1f 100644 --- a/src/routes/reference/secondary-primitives/create-computed.mdx +++ b/src/routes/reference/secondary-primitives/create-computed.mdx @@ -11,12 +11,12 @@ tags: - tracking version: "1.0" description: >- - Create immediate reactive computations with createComputed. Build custom - primitives and handle side effects that respond to dependencies. + Create an immediate reactive computation that runs synchronously when created + and whenever its dependencies change. --- -The `createComputed` function creates a reactive computation that runs _before_ the rendering phase. -It is primarily used to synchronize state before rendering begins. +`createComputed` creates an immediate reactive computation. +It runs synchronously in the current execution context when created, then re-runs whenever its tracked dependencies change. ## Import @@ -83,41 +83,39 @@ It is used for identification in debugging tools like the [Solid Debugger](https `createComputed` does not return a value. +## Behavior + +- `createComputed` runs immediately when it is created. +- It tracks reactive reads inside `fn` and re-runs synchronously when those dependencies change. +- Unlike [`createMemo`](/reference/basic-reactivity/create-memo), it does not expose a derived accessor. +- `createComputed` is primarily used to build reactive primitives. Application code that derives state usually wants [`createMemo`](/reference/basic-reactivity/create-memo) instead. + ## Examples -### Basic usage +### Build a writable derived signal ```tsx -import { createComputed } from "solid-js"; -import { createStore } from "solid-js/store"; +import { createComputed, createSignal } from "solid-js"; -type User = { - name?: string; -}; +function createWritableMemo<T>(fn: () => T) { + const [value, setValue] = createSignal(fn()); -type UserEditorProps = { - user: User; -}; - -function UserEditor(props: UserEditorProps) { - const [formData, setFormData] = createStore<User>({ - name: "", - }); - - // Update the store synchronously when props change. - // This prevents a second render cycle. createComputed(() => { - setFormData("name", props.user.name); + setValue(fn()); }); + return value; +} + +function Counter() { + const [count, setCount] = createSignal(1); + const double = createWritableMemo(() => count() * 2); + return ( - <form> - <h1>Editing: {formData.name}</h1> - <input - value={formData.name} - onInput={(e) => setFormData("name", e.currentTarget.value)} - /> - </form> + <> + <p>{double()}</p> + <button onClick={() => setCount((value) => value + 1)}>Increment</button> + </> ); } ``` diff --git a/src/routes/reference/secondary-primitives/create-deferred.mdx b/src/routes/reference/secondary-primitives/create-deferred.mdx index 9f7debad4..5dfb8bc09 100644 --- a/src/routes/reference/secondary-primitives/create-deferred.mdx +++ b/src/routes/reference/secondary-primitives/create-deferred.mdx @@ -11,30 +11,100 @@ 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 whose updates are scheduled for later execution 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> = () => T; function createDeferred<T>( - source: () => T, + source: Accessor<T>, options?: { timeoutMs?: number; equals?: false | ((prev: T, next: T) => boolean); name?: string; } -): () => T; +): Accessor<T>; ``` -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<T>` +- **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<T>` + +Returns an accessor that exposes the deferred value. + +## Behavior + +- The deferred accessor initially reflects the current source value. +- Later updates are deferred through Solid's scheduler until later execution or until `timeoutMs` is reached, so the returned accessor can lag behind the source accessor. +- `equals` controls whether downstream dependents are notified for a new value. +- `createDeferred` defers propagation of the accessor value. It does not debounce writes to the source accessor. +- On the server, `createDeferred` returns the source accessor unchanged. + +## Examples + +### Basic usage + +```tsx +import { createDeferred, createSignal } from "solid-js"; + +function Example() { + const [value, setValue] = createSignal(""); + const deferredValue = createDeferred(value); + + return ( + <> + <input + value={value()} + onInput={(event) => setValue(event.currentTarget.value)} + /> + <div>{deferredValue()}</div> + </> + ); +} +``` -## 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 b29ccf7b9..7cfa6a092 100644 --- a/src/routes/reference/secondary-primitives/create-reaction.mdx +++ b/src/routes/reference/secondary-primitives/create-reaction.mdx @@ -11,28 +11,73 @@ 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` + +#### `name` + +- **Type:** `string` + +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 + +- Each call to the returned function records dependencies read during `tracking`. +- `onInvalidate` runs untracked on the first change to any recorded dependency. +- On the server, the returned function just executes `tracking`. + +## 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) diff --git a/src/routes/reference/secondary-primitives/create-render-effect.mdx b/src/routes/reference/secondary-primitives/create-render-effect.mdx index 03ba5016b..aac6df1a1 100644 --- a/src/routes/reference/secondary-primitives/create-render-effect.mdx +++ b/src/routes/reference/secondary-primitives/create-render-effect.mdx @@ -12,12 +12,11 @@ tags: - lifecycle version: "1.0" description: >- - Execute effects immediately during rendering with createRenderEffect. Run side - effects as DOM creates, before refs are set or connected. + Create a reactive computation that runs immediately during the render phase. --- -The `createRenderEffect` primitive creates a reactive computation that automatically tracks reactive values, such as [signals](/concepts/signals), accessed within the provided function. -This function re-runs whenever any of its dependencies change. +`createRenderEffect` creates a reactive computation that runs during the render phase as DOM is created and updated. +It tracks reactive reads inside the provided function and re-runs whenever those dependencies change. ## Execution Timing @@ -30,15 +29,12 @@ This function re-runs whenever any of its dependencies change. ### Subsequent Runs - After the initial render, the render effect **re-runs whenever any of its tracked dependencies change**. -- Re-runs occur **after** all pure computations (such as [memos](/concepts/derived-values/memos)) have completed within the same update cycle. - When multiple dependencies change within the same batch, the render effect **runs once per batch**. -- The **order of re-runs** among multiple render effects is **not guaranteed**. ### Server-Side Rendering - During SSR, render effects **run once on the server**, since they are part of the synchronous rendering phase. -- On the client, an initial run still occurs during the client rendering phase to initialize the reactive system; - that client initial run is separate from the server run. +- On the client, an initial run still occurs during the client rendering phase. - After hydration, subsequent runs occur on the client when dependencies change. ## Import @@ -69,7 +65,7 @@ function createRenderEffect<Next, Init>( ### `fn` -- **Type:** `EffectFunction<undefined | NoInfer<Next> | EffectFunction<Init | Next, Next>` +- **Type:** `EffectFunction<undefined | NoInfer<Next>, Next> | EffectFunction<Init | Next, Next>` - **Required:** Yes A function to be executed as the render effect. @@ -102,69 +98,33 @@ A name for the render effect, which can be useful for identification in debuggin `createRenderEffect` does not return a value. -## Examples - -### Basic Usage - -```tsx -import { createSignal, createRenderEffect } from "solid-js"; +## Behavior -function Counter() { - const [count, setCount] = createSignal(0); +- `createRenderEffect` runs immediately when it is created. +- Its initial run happens during the render phase, before mounted DOM is connected and before refs are assigned. +- Later runs happen when tracked dependencies change. +- Most application code should use [`createEffect`](/reference/basic-reactivity/create-effect). `createRenderEffect` is mainly for render-phase work where that timing is required. - // This runs immediately during render, and re-runs when the count changes. - createRenderEffect(() => { - console.log("Count: ", count()); - }); - - return ( - <div> - <p>Count: {count()}</p> - <button onClick={() => setCount((prev) => prev + 1)}>Increment</button> - </div> - ); -} -``` +## Examples -### Execution Timing +### Ref timing ```tsx -import { createSignal, createEffect, createRenderEffect } from "solid-js"; - -function Counter() { - const [count, setCount] = createSignal(0); - - // This is part of the component's synchronous execution. - console.log("Hello from counter"); +import { createRenderEffect, onMount } from "solid-js"; - // This effect is scheduled to run after the initial render is complete. - createEffect(() => { - console.log("Effect:", count()); - }); +function Example() { + let element: HTMLDivElement | undefined; - // By contrast, a render effect runs synchronously during the render phase. createRenderEffect(() => { - console.log("Render effect:", count()); + console.log("render effect", element); // undefined on the initial run }); - // Setting a signal during the render phase re-runs render effects, but not effects, which are - // still scheduled. - setCount(1); - - // A microtask is scheduled to run after the current synchronous code (the render phase) finishes. - queueMicrotask(() => { - // Now that rendering is complete, signal updates will trigger effects immediately. - setCount(2); + onMount(() => { + console.log("mounted", element); // <div> }); -} -// Output: -// Hello from counter -// Render effect: 0 -// Render effect: 1 -// Effect: 1 -// Render effect: 2 -// Effect: 2 + return <div ref={element}>Hello</div>; +} ``` ## Related diff --git a/src/routes/reference/secondary-primitives/create-selector.mdx b/src/routes/reference/secondary-primitives/create-selector.mdx index a2b4e7ece..3b43ec360 100644 --- a/src/routes/reference/secondary-primitives/create-selector.mdx +++ b/src/routes/reference/secondary-primitives/create-selector.mdx @@ -11,66 +11,94 @@ 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> = () => T; -function createSelector<T, U>( - source: () => T, - fn?: (a: U, b: T) => boolean +function createSelector<T, U = T>( + source: Accessor<T>, + 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` -<For each={list()}> - {(item) => <li classList={{ active: isSelected(item.id) }}>{item.name}</li>} -</For> -``` +- **Type:** `Accessor<T>` +- **Required:** Yes + +Accessor used as the selection source. + +### `fn` + +- **Type:** `(key: U, value: T) => boolean` + +Comparison function used to match a key against the current source value. -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. +### `options` -By contrast, the following code would perform `list().length` DOM operations -every time the `selectedId` signal changes: +#### `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. Each subscriber tracks only its own key. + +## Behavior + +- The returned function compares each key against the current source value. With the default comparison, matching uses strict equality. +- Solid tracks subscribers by key, so only subscribers whose key starts or stops matching need to update. +- On the server, `createSelector` compares each key directly against `source()` without keyed subscriber bookkeeping. + +Compared with checking equality directly in every subscriber, `createSelector` keeps subscriptions keyed by the compared value. + +## Examples + +### Basic usage ```tsx -const [selectedId, setSelectedId] = createSignal() +import { createSelector, createSignal, For } from "solid-js"; + +function Example(props) { + const [selectedId, setSelectedId] = createSignal<number>(); + const isSelected = createSelector(selectedId); -<For each={list()}> - {(item) => <li classList={{ active: selectedId() === item.id }}>{item.name}</li>} -</For> + return ( + <For each={props.list}> + {(item) => ( + <li + classList={{ active: isSelected(item.id) }} + onClick={() => setSelectedId(item.id)} + > + {item.name} + </li> + )} + </For> + ); +} ``` -## 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) +- [`<For>`](/reference/components/for) diff --git a/src/routes/reference/server-utilities/get-request-event.mdx b/src/routes/reference/server-utilities/get-request-event.mdx index 15afc8f38..37a00c6fb 100644 --- a/src/routes/reference/server-utilities/get-request-event.mdx +++ b/src/routes/reference/server-utilities/get-request-event.mdx @@ -1,82 +1,56 @@ --- title: getRequestEvent use_cases: >- - server-side rendering, authentication, headers manipulation, cookies, server - functions, request context access + reading the current request object, accessing request-scoped server context tags: - server - ssr - request - - headers - - authentication - context version: "1.0" description: >- - Access server request context with getRequestEvent. Read headers, set cookies, - and manage response status in SSR and server functions. + Read the current request event from `solid-js/web`. --- -Solid uses Async Local Storage as a way of injecting the request context anywhere on the server. -The server provides a utility function to access this context -(called a `RequestEvent`). +`getRequestEvent` returns the current request event when one is available. -```js -import { getRequestEvent } from "solid-js/web" -import type { RequestEvent } from "solid-js/web" +## Import -function getRequestEvent(): RequestEvent | undefined +```ts +import { getRequestEvent } from "solid-js/web"; ``` -You can retrieve the request event by calling `getRequestEvent`: +## Type -```js -import { getRequestEvent } from "solid-js/web"; +```ts +import type { RequestEvent } from "solid-js/web"; -const event = getRequestEvent(); +function getRequestEvent(): RequestEvent | undefined; ``` -## Request +## Return value -`.request` is the most important property of the `RequestEvent`. -This is a Web [Request object](https://developer.mozilla.org/en-US/docs/Web/API/Request) that represents the current request to the server. -You can access properties off of it such as `url` and `headers`. -`body`, however, does not typically need to be handled directly for things such as server functions or rendering, which already handle mapping. +- **Type:** `RequestEvent | undefined` -```js -import { getRequestEvent } from "solid-js/web"; +## Behavior -const event = getRequestEvent(); -if (event) { - const auth = event.request.headers.get("Authorization"); -} -``` +- `getRequestEvent` is for managed server/request scope. +- When available, the returned event includes the current `Request` as `event.request`. +- Depending on the server integration, the event can also expose request-scoped fields such as `response`, `locals`, or router state. +- If no current request event is available, including outside managed async scope, `getRequestEvent` returns `undefined`. -## Response +## Examples -The `getRequestEvent` can also be used to stub out the Response - this extends the [options that can be passed to the `Response constructor`](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#options). -This is kept up to date so it can be used to read and write headers and status for the current response. +### Basic usage -```js +```ts import { getRequestEvent } from "solid-js/web"; -const event = getRequestEvent(); -if (event) { - event.response.headers.append("Set-Cookie", "foo=hello"); - event.response.status = 201; +function readAuthorizationHeader() { + const event = getRequestEvent(); + + return event?.request.headers.get("Authorization") ?? null; } ``` -### Change event.response or create a new Response - -The `getRequestEvent` event is considered global and lasts the life of the request. -Therefore, if you are calling a server function on the server during SSR or an RPC call, setting values on `event.response` will reflect on that request. - -The returned response will only impact the response when it is an RPC call. -This is important because some headers previously set may not be needed to be set for the whole page, but only for a specific request. - -**Note:** This is important to keep in mind when choosing where to set headers and responses. - -:::note[Usage with SolidStart] -See this guide on [Request -Events](/solid-start/advanced/request-events). -::: +This example reads the event during request-scoped server execution. diff --git a/src/routes/reference/store-utilities/create-mutable.mdx b/src/routes/reference/store-utilities/create-mutable.mdx index 19889ecb1..d04ae0900 100644 --- a/src/routes/reference/store-utilities/create-mutable.mdx +++ b/src/routes/reference/store-utilities/create-mutable.mdx @@ -1,38 +1,67 @@ --- title: createMutable use_cases: >- - mobx migration, vue compatibility, external system integration, mutable state - patterns, proxy-based reactivity + mutable proxy state, interop with mutable systems, object mutation through a + store proxy tags: - store - mutable - proxy - state - - compatibility version: "1.0" description: >- - Create mutable proxy stores with automatic deep tracking. Ideal for MobX/Vue - compatibility or integrating external mutable systems. + Create a mutable store proxy. --- -`createMutable` creates a new mutable Store proxy object that provides a way to selectively trigger updates only when values change. +`createMutable` creates a mutable store proxy. -By intercepting property access, it allows automatic tracking of deep nesting via proxy making it useful for integrating external systems or serving as a compatibility layer with frameworks like MobX or Vue. +## Import -```tsx +```ts import { createMutable } from "solid-js/store"; -import type { Store, StoreNode } from "solid-js/store"; +``` + +## Type -function createMutable<T extends StoreNode>(state: T | Store<T>): Store<T>; +```ts +function createMutable<T extends StoreNode>( + state: T, + options?: { name?: string } +): T; ``` -:::note -It's important to recognize that a mutable state, which can be passed around and modified anywhere, may complicate the code structure and increase the risk of breaking unidirectional flow. +## Parameters + +### `state` + +- **Type:** `T` + +Initial mutable state. + +### `options` + +#### `name` + +- **Type:** `string` + +Debug name used by development tooling. + +## Return value - For a more robust alternative, it is generally recommended to use `createStore` instead. - Additionally, the [`produce`](/reference/store-utilities/produce) utility can provide many of these same benefits without the associated downsides. +- **Type:** `T` -::: +Mutable store proxy. + +## Behavior + +- `createMutable` creates mutable shared state through a reactive proxy. Property reads and writes go through that proxy, and nested property access is reactive. +- Writes, deletes, and array mutator methods are batched through the proxy while updating the store in place. +- `createMutable` exposes reads and writes through the same proxy instead of separating them into a getter and setter. +- Getters and setters defined on the initial object remain available on the mutable store. + +## Examples + +### Basic usage ```tsx import { createMutable } from "solid-js/store"; @@ -42,26 +71,30 @@ const state = createMutable({ list: [], }); -// read value -state.someValue; - -// set value state.someValue = 5; - -state.list.push(anotherValue); +state.list.push("item"); ``` -Mutables support setters along with getters. +### Getter and setter ```tsx +import { createMutable } from "solid-js/store"; + const user = createMutable({ firstName: "John", lastName: "Smith", get fullName() { return `${this.firstName} ${this.lastName}`; }, - set setFullName(value) { + set fullName(value) { [this.firstName, this.lastName] = value.split(" "); }, }); + +user.fullName = "Jane Doe"; ``` + +## Related + +- [`modifyMutable`](/reference/store-utilities/modify-mutable) +- [`createStore`](/reference/store-utilities/create-store) diff --git a/src/routes/reference/store-utilities/create-store.mdx b/src/routes/reference/store-utilities/create-store.mdx index 5f17cc553..180b7ddd0 100644 --- a/src/routes/reference/store-utilities/create-store.mdx +++ b/src/routes/reference/store-utilities/create-store.mdx @@ -1,101 +1,104 @@ --- title: createStore use_cases: >- - complex state management, nested data, arrays and objects, derived values, - application state + object state, array state, nested state, structured application state tags: - store - state - - data-structures - objects - arrays version: "1.0" description: >- - Manage complex application state with createStore. Handle nested objects, - arrays, and derived values with fine-grained reactivity. + Create a reactive store and a setter function for structured state. --- -Stores were intentionally designed to manage data structures like objects and arrays but are capable of handling other data types, such as strings and numbers. +`createStore` creates a reactive store and setter function for structured state. -## Types Signature +## Import -```tsx +```ts import { createStore } from "solid-js/store"; -import type { StoreNode, Store, SetStoreFunction } from "solid-js/store"; +``` + +## Type -function createStore<T extends StoreNode>( - state: T | Store<T> -): [get: Store<T>, set: SetStoreFunction<T>]; +```ts +type Store<T> = T; -type Store<T> = T; // conceptually readonly, but not typed as such +interface SetStoreFunction<T> { + (setter: T | Partial<T> | ((prev: T) => T | Partial<T>)): void; + (...path: unknown[]): void; +} + +function createStore<T extends object = {}>( + store?: T | Store<T>, + options?: { name?: string } +): [Store<T>, SetStoreFunction<T>]; ``` -## Usage +## Parameters -```tsx -import { createStore } from "solid-js/store"; +### `store` -// Initialize store -const [store, setStore] = createStore({ - userCount: 3, - users: [ - { - id: 0, - username: "felix909", - location: "England", - loggedIn: false, - }, - { - id: 1, - username: "tracy634", - location: "Canada", - loggedIn: true, - }, - { - id: 1, - username: "johny123", - location: "India", - loggedIn: true, - }, - ], -}); -``` +- **Type:** `T | Store<T>` + +Initial store value. + +### `options` + +#### `name` + +- **Type:** `string` + +Debug name used by development tooling. + +## Return value + +- **Type:** `[Store<T>, SetStoreFunction<T>]` + +Tuple containing the store proxy and its setter. + +## Behavior + +- The returned store is read through a proxy, and property reads track at the property level. +- The setter supports both top-level updates and path syntax for nested updates. +- Object updates shallow-merge by default, while single-array updates replace array contents. Setting a property to `undefined` deletes it. +- Getters defined on the initial object remain available on the store. -## Getter +## Examples -Store objects support the use of getters to store derived values. +### Basic usage ```tsx +import { createStore } from "solid-js/store"; + const [state, setState] = createStore({ user: { firstName: "John", lastName: "Smith", - get fullName() { - return `${this.firstName} ${this.lastName}`; - }, }, }); -``` -## Setter +setState("user", "firstName", "Jane"); +``` -Changes can take the form of function that passes previous state and returns new state or a value. -Objects are always shallowly merged. Set values to undefined to delete them from the Store. -In TypeScript, you can delete a value by using a non-null assertion, like `undefined!`. +### Getter ```tsx -const [state, setState] = createStore({ - firstName: "John", - lastName: "Miller", -}); - -setState({ firstName: "Johnny", middleName: "Lee" }); -// ({ firstName: 'Johnny', middleName: 'Lee', lastName: 'Miller' }) +import { createStore } from "solid-js/store"; -setState((state) => ({ preferredName: state.firstName, lastName: "Milner" })); -// ({ firstName: 'Johnny', preferredName: 'Johnny', middleName: 'Lee', lastName: 'Milner' }) +const [state] = createStore({ + user: { + firstName: "John", + lastName: "Smith", + get fullName() { + return `${this.firstName} ${this.lastName}`; + }, + }, +}); ``` ---- +## Related -To learn more about using stores check the [Stores Guide](/concepts/stores), and the **Store utilities** section for more advanced APIs. +- [`createMutable`](/reference/store-utilities/create-mutable) +- [Stores](/concepts/stores) diff --git a/src/routes/reference/store-utilities/modify-mutable.mdx b/src/routes/reference/store-utilities/modify-mutable.mdx index 0d8db0e49..7673917de 100644 --- a/src/routes/reference/store-utilities/modify-mutable.mdx +++ b/src/routes/reference/store-utilities/modify-mutable.mdx @@ -1,92 +1,68 @@ --- title: modifyMutable use_cases: >- - batch updates, mutable store modifications, performance optimization, multiple - field changes, reconciliation + batched mutable-store updates, applying store modifiers to mutable stores tags: - store - mutable - batch - - performance - updates version: "1.0" description: >- - Batch multiple mutable store changes with modifyMutable. Update multiple - fields in one render cycle for better performance. + Apply a modifier to a mutable store inside a batch. --- -`modifyMutable` streamlines the process of making multiple changes to a mutable Store, as obtained through the use of [`createMutable`](/reference/store-utilities/create-mutable). +`modifyMutable` applies a modifier to a mutable store inside a batch. -It operates within a single [`batch`](/reference/reactive-utilities/batch), ensuring that dependent computations are updated just once, rather than triggering updates for each individual change. +## Import -```tsx +```ts import { modifyMutable } from "solid-js/store"; - -function modifyMutable<T>(mutable: T, modifier: (state: T) => T): void; ``` -The function takes two arguments: +## Type -1. The first argument is the mutable Store that needs modification. -2. The second argument is a Store modifier, which could be one of those returned by [`reconcile`](/reference/store-utilities/reconcile). +```ts +function modifyMutable<T>(state: T, modifier: (state: T) => T): void; +``` -:::caution -When passing in your own modifier function, it's important to be aware that -its argument is an unwrapped version of the store. -::: +## Parameters -For example, if the UI depends on multiple fields of a mutable: +### `state` -```tsx -import { createMutable } from "solid-js/store"; +- **Type:** `T` -const state = createMutable({ - user: { - firstName: "John", - lastName: "Smith", - }, -}); +Mutable store to modify. -<h1>Hello {state.user.firstName + " " + state.user.lastName}</h1>; -``` +### `modifier` -Modifying n fields in sequence will cause the UI to update n times: +- **Type:** `(state: T) => T` -```tsx -state.user.firstName = "Jane"; -state.user.lastName = "Doe"; -``` +Modifier applied to the mutable store. For direct mutation-style modifiers, the return value is ignored. -To trigger just a single update, the fields can be modified using a `batch`: +## Return value -```tsx -import { batch } from "solid-js"; +- **Type:** `void` -batch(() => { - state.user.firstName = "Jane"; - state.user.lastName = "Doe"; -}); -``` +## Behavior -`modifyMutable` combined with [`reconcile`](/reference/store-utilities/reconcile) or [`produce`](/reference/store-utilities/produce) provides two alternate ways to do similar things: +- `modifyMutable` runs inside a [`batch`](/reference/reactive-utilities/batch), so multiple changes notify dependents after the modifier completes, and the modifier receives the unwrapped underlying state object instead of the proxy. +- The modifier can be a function returned by helpers such as [`reconcile`](/reference/store-utilities/reconcile) or [`produce`](/reference/store-utilities/produce). -```tsx -import { modifyMutable, reconcile } from "solid-js/store"; +## Examples -// Replace state.user with the specified object (deleting any other fields) -modifyMutable( - state.user, - reconcile({ - firstName: "Jane", - lastName: "Doe", - }) -); -``` +### Basic usage ```tsx -import { modifyMutable, produce } from "solid-js/store"; +import { createMutable, modifyMutable, produce } from "solid-js/store"; + +const state = createMutable({ + user: { + firstName: "John", + lastName: "Smith", + }, +}); -// Modify two fields in a batch, triggering just one update modifyMutable( state, produce((state) => { @@ -95,3 +71,9 @@ modifyMutable( }) ); ``` + +## Related + +- [`createMutable`](/reference/store-utilities/create-mutable) +- [`produce`](/reference/store-utilities/produce) +- [`reconcile`](/reference/store-utilities/reconcile) diff --git a/src/routes/reference/store-utilities/produce.mdx b/src/routes/reference/store-utilities/produce.mdx index 447b4889b..efd55d775 100644 --- a/src/routes/reference/store-utilities/produce.mdx +++ b/src/routes/reference/store-utilities/produce.mdx @@ -1,50 +1,73 @@ --- title: produce use_cases: >- - immutable updates, immer-style mutations, store modifications, complex state - changes, nested updates + store updates, nested store changes, draft-style mutations tags: - store - - immutable - updates - - immer - mutations version: "1.0" description: >- - Use Immer-inspired produce API to mutate Solid stores immutably. Simplify - complex nested updates with familiar mutation syntax. + Create a store modifier that applies changes by mutating the current state through a helper proxy. --- -`produce` is an [Immer](https://immerjs.github.io/immer/) inspired API for Solid's Store objects that allows the store to be mutated inside the `produce` function. +`produce` creates a store modifier that applies changes by mutating the current state through a helper proxy. + +## Import ```ts import { produce } from "solid-js/store"; -import type { NotWrappable, Store } from "solid-js/store"; +``` + +## Type -function produce<T>( - fn: (state: T) => void -): ( - state: T extends NotWrappable ? T : Store<T> -) => T extends NotWrappable ? T : Store<T>; +```ts +function produce<T>(fn: (state: T) => void): (state: T) => T; ``` -For use with `createStore`: +## Parameters + +### `fn` + +- **Type:** `(state: T) => void` + +Function that mutates the provided proxy state. + +## Return value + +- **Type:** `(state: T) => T` + +Store modifier function. + +## Behavior + +- `produce` returns a function that can be passed to store setters or [`modifyMutable`](/reference/store-utilities/modify-mutable). +- The returned modifier mutates the passed state and returns that same state. +- The helper is primarily for store objects and nested wrappable data. + +## Examples + +### Basic usage ```tsx -import { produce } from "solid-js/store"; +import { createStore, produce } from "solid-js/store"; const [state, setState] = createStore({ user: { name: "John", - age: 30, }, - list: ["book", "pen"], + list: ["book"], }); setState( produce((state) => { state.user.name = "Jane"; - state.list.push("pencil"); + state.list.push("pen"); }) ); ``` + +## Related + +- [`modifyMutable`](/reference/store-utilities/modify-mutable) +- [`reconcile`](/reference/store-utilities/reconcile) diff --git a/src/routes/reference/store-utilities/reconcile.mdx b/src/routes/reference/store-utilities/reconcile.mdx index 11ad1cc8d..000e7ffc4 100644 --- a/src/routes/reference/store-utilities/reconcile.mdx +++ b/src/routes/reference/store-utilities/reconcile.mdx @@ -1,58 +1,83 @@ --- title: reconcile use_cases: >- - api responses, data synchronization, immutable data diffing, observable - subscriptions, large data updates + syncing store state, applying immutable data updates, diffing incoming data tags: - store - diffing - - api - synchronization - - immutable version: "1.0" description: >- - Efficiently diff and merge data changes with reconcile. Perfect for syncing - API responses or handling immutable data updates. + Create a store modifier that reconciles existing state with a new value. --- -`reconcile` is designed for diffing data changes in situations where granular updates cannot be applied. -This is useful when dealing with immutable data from stores or handling large API responses. +`reconcile` creates a store modifier that reconciles existing state with a new value. -```tsx +## Import + +```ts import { reconcile } from "solid-js/store"; -import type { NotWrappable, Store } from "solid-js/store"; +``` -function reconcile<T>( - value: T | Store<T>, +## Type + +```ts +function reconcile<T extends U, U>( + value: T, options?: { key?: string | null; merge?: boolean; - } = { key: "id" } -): ( - state: T extends NotWrappable ? T : Store<T> -) => T extends NotWrappable ? T : Store<T>; + } +): (state: U) => T; ``` -`reconcile` has a `key` option that can be used when available to match items. -The `value` accepts either a value of type `T` or a Store containing values of type `T`. -This represents the data to be reconciled. +## Parameters + +### `value` + +- **Type:** `T` + +New value to reconcile against the current state. + +### `options` + +#### `key` + +- **Type:** `string | null` -The `reconcile` function helps manage data changes by performing a diffing process, making it particularly handy in scenarios where applying granular updates is challenging or inefficient. +Key used to match items during reconciliation. -The `key` and `merge` options provide flexibility to customize the reconciliation process based on specific needs. +#### `merge` + +- **Type:** `boolean` + +Controls whether reconciliation pushes updates to the leaves instead of replacing non-matching branches. + +## Return value + +- **Type:** `(state: U) => T` + +Store modifier function. + +## Behavior + +- `reconcile` is for reconciling an incoming snapshot against existing store state. +- With the default `key`, array items are matched by `"id"` where possible and otherwise fall back to positional or reference matching. +- Non-wrappable values are replaced directly. +- When `merge` is `false`, reconciliation prefers replacing non-matching branches. When `merge` is `true`, it pushes updates deeper into the structure. + +## Examples + +### Basic usage ```ts -// subscribing to an observable -const unsubscribe = store.subscribe(({ todos }) => ( - setState('todos', reconcile(todos)); -); -onCleanup(() => unsubscribe()); +import { createStore, reconcile } from "solid-js/store"; + +const [state, setState] = createStore({ todos: [] }); +setState("todos", reconcile([{ id: 1, title: "Write docs" }])); ``` -##### Options +## Related -| Option | Type | Default | Description | -| ------ | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| key | string | "id" | Specifies the key to be used for matching items during reconciliation | -| merge | boolean | false | When merge is false, referential checks are performed where possible to determine equality, and items that are not referentially equal are replaced. When merge is true, all diffing is pushed to the leaves, effectively morphing the previous data to the new value. | +- [`produce`](/reference/store-utilities/produce) diff --git a/src/routes/reference/store-utilities/unwrap.mdx b/src/routes/reference/store-utilities/unwrap.mdx index 7ca79f2ae..551e26acf 100644 --- a/src/routes/reference/store-utilities/unwrap.mdx +++ b/src/routes/reference/store-utilities/unwrap.mdx @@ -1,25 +1,66 @@ --- title: unwrap use_cases: >- - raw data access, proxy removal, debugging, third-party integration, data - serialization + reading store data without proxies, passing plain data to other APIs tags: - store - proxy - data - - debugging - - utilities version: "1.0" description: >- - Extract raw data from Solid stores without proxy wrapping. Essential for - debugging, serialization, or third-party integrations. + Remove store proxy wrapping from a store or store subtree. --- -`unwrap` returns the underlying data in the store without a proxy. +`unwrap` removes store proxy wrapping from a store or store subtree. -```tsx +## Import + +```ts import { unwrap } from "solid-js/store"; -import type { Store } from "solid-js/store"; +``` + +## Type -function unwrap(store: Store<T>): T; +```ts +function unwrap<T>(item: T): T; ``` + +## Parameters + +### `item` + +- **Type:** `T` + +Store value or store subtree to unwrap. + +## Return value + +- **Type:** `T` + +Unwrapped value. + +## Behavior + +- `unwrap` removes store proxies recursively and returns underlying plain data, reusing existing objects or arrays instead of cloning them when possible. +- Frozen objects and arrays are shallow-copied before recursive unwrapping, while mutable ones are unwrapped in place. +- Non-proxy input values are returned unchanged. +- Mutating the returned value can mutate the underlying store data. + +Do not assume `unwrap` produces an isolated deep clone. + +## Examples + +### Basic usage + +```ts +import { createStore, unwrap } from "solid-js/store"; + +const [state] = createStore({ user: { name: "John" } }); +const user = unwrap(state.user); + +user.name = "Jane"; +``` + +## Related + +- [`createStore`](/reference/store-utilities/create-store)