diff --git a/scripts/extract-solid-ref.mjs b/scripts/extract-solid-ref.mjs index c9245c404..d41b8dd75 100644 --- a/scripts/extract-solid-ref.mjs +++ b/scripts/extract-solid-ref.mjs @@ -147,6 +147,10 @@ const CANONICAL_ROUTES = { Switch: ["components-jsx/switch-and-match.mdx", "Components (JSX)"], clientOnly: ["rendering-ssr/client-only.mdx", "Rendering & SSR"], + getHydrationWriter: [ + "rendering-ssr/get-hydration-writer.mdx", + "Rendering & SSR", + ], httpHeader: ["rendering-ssr/http-header.mdx", "Rendering & SSR"], httpStatus: ["rendering-ssr/http-status.mdx", "Rendering & SSR"], hydrate: ["rendering-ssr/hydrate.mdx", "Rendering & SSR"], @@ -155,6 +159,10 @@ const CANONICAL_ROUTES = { render: ["rendering-ssr/render.mdx", "Rendering & SSR"], renderToStream: ["rendering-ssr/render-to-stream.mdx", "Rendering & SSR"], renderToString: ["rendering-ssr/render-to-string.mdx", "Rendering & SSR"], + takeHydrationValue: [ + "rendering-ssr/take-hydration-value.mdx", + "Rendering & SSR", + ], GET: ["server-functions/get.mdx", "Server functions"], live: ["server-functions/live.mdx", "Server functions"], @@ -283,6 +291,14 @@ const ADVANCED_ROUTES = { "advanced/manual-hydration/hydration.mdx", "Advanced / Manual Hydration", ], + isHydratable: [ + "advanced/manual-hydration/is-hydratable.mdx", + "Advanced / Manual Hydration", + ], + isHydrating: [ + "advanced/manual-hydration/is-hydrating.mdx", + "Advanced / Manual Hydration", + ], NoHydration: [ "advanced/manual-hydration/no-hydration.mdx", "Advanced / Manual Hydration", @@ -457,11 +473,14 @@ const FOLD_INTO = { RecordEvent: "OBSERVE", RecordLive: "OBSERVE", RecordListener: "OBSERVE", + RecordSubscribeOptions: "OBSERVE", BoundaryEvent: "OBSERVE", BoundaryListener: "OBSERVE", BoundaryLive: "OBSERVE", CallEvent: "OBSERVE", CallListener: "OBSERVE", + CallRequestEvent: "OBSERVE", + CallRequestListener: "OBSERVE", CallLive: "OBSERVE", FrameEvent: "OBSERVE", FrameProducedEvent: "OBSERVE", @@ -477,6 +496,7 @@ const FOLD_INTO = { RenderEvent: "OBSERVE", RenderListener: "OBSERVE", RenderLive: "OBSERVE", + RenderRoute: "OBSERVE", // solid-js/attribution: the engine, its options and its records. The // records are delivered on OBSERVE.records; their shapes live here. Attribution: "attribution", @@ -575,6 +595,8 @@ const FOLD_INTO = { RequestEvent: "getRequestEvent", RequestEventLocals: "getRequestEvent", ResponseStub: "getRequestEvent", + HydrationWriter: "getHydrationWriter", + HydrationValue: "takeHydrationValue", ServerFunction: "withMeta", ServerFunctionMetadata: "withMeta", LiveSource: "live", @@ -602,6 +624,13 @@ const FOLD_INTO = { }; const HIDDEN_EXPORTS = new Set([ + // Compiler slot protocol; not part of the application-facing reference. + "SLOT_VALUE", + "SLOT_MARKER", + "SLOT_FACE_STREAM", + "SLOT_FACE_DATA", + "SLOT_FACE_MARKUP", + "isSlotValue", "$DEVCOMP", "$PROXY", "$REFRESH", @@ -1026,6 +1055,7 @@ const VALUE_IMPORTS = new Set(["storePath"]); // Exports with a client stub and a server implementation: the page reads // from the implementation. const PREFERRED_SOURCE_PATHS = { + getHydrationWriter: "packages/web/src/server.ts", getRequestEvent: "packages/web/src/server.ts", getTraceContext: "packages/web/src/server.ts", configureServerErrors: "packages/web/src/server.ts", @@ -2560,6 +2590,10 @@ const COLLAPSED_RELATED_TYPES = new Set([ ]); const REFERENCE_FIXUPS = [ + [ + /onSettled\(\(\) => setWidth\(el\.offsetWidth\)\);/g, + "onSettled(() => { setWidth(el.offsetWidth); });", + ], [ /createSignal\(fn, initialValue\?, options\?:/g, "createSignal(fn, options?:", diff --git a/src/routes/reference/(1)solid-js/(6)advanced/(5)manual-hydration/is-hydratable.mdx b/src/routes/reference/(1)solid-js/(6)advanced/(5)manual-hydration/is-hydratable.mdx new file mode 100644 index 000000000..3e154ebce --- /dev/null +++ b/src/routes/reference/(1)solid-js/(6)advanced/(5)manual-hydration/is-hydratable.mdx @@ -0,0 +1,49 @@ +--- +title: "isHydratable" +category: "Advanced / Manual Hydration" +use_cases: "advanced manual hydration api, ishydratable usage" +tags: + - "is" + - "hydratable" + - "advanced" + - "manual" + - "hydration" + - "reference" + - "api" + - "v2" +version: "2.0" +description: "Whether the calling owner sits where hydration applies — not under ``, or back under a nested `` — so a value keyed to this position reaches a hydrating client." +source_repo: "solidjs/solid" +source_ref: "next" +source_path: "packages/solid/src/client/hydration.ts" +--- + +{/* Generated by scripts/extract-solid-ref.mjs. Edit the source JSDoc or disposition map, then regenerate. */} + +Whether the calling owner sits where hydration applies — not under +``, or back under a nested `` — so a value keyed +to this position reaches a hydrating client. On the server it also +requires the owner to belong to a render in progress. `false` with no +owner (a promise continuation, an IO callback): read it where the owner +is known. On the client, `` is a passthrough, so inside a +`` zone (which renders only outside hydration) this stays +`false` even under a nested ``. + +Solid decides this itself for its own values. A library writing keyed +values with `getHydrationWriter()` reads it to make the same decision. + +## Import + +```ts +import { isHydratable } from "solid-js"; +``` + +## Type signature + +```ts +function isHydratable(): boolean; +``` + +## Learn more + +- [Controlling hydration](/concepts/rendering-and-ssr#controlling-hydration) diff --git a/src/routes/reference/(1)solid-js/(6)advanced/(5)manual-hydration/is-hydrating.mdx b/src/routes/reference/(1)solid-js/(6)advanced/(5)manual-hydration/is-hydrating.mdx new file mode 100644 index 000000000..9c17cc457 --- /dev/null +++ b/src/routes/reference/(1)solid-js/(6)advanced/(5)manual-hydration/is-hydrating.mdx @@ -0,0 +1,56 @@ +--- +title: "isHydrating" +category: "Advanced / Manual Hydration" +use_cases: "advanced manual hydration api, ishydrating usage" +tags: + - "is" + - "hydrating" + - "advanced" + - "manual" + - "hydration" + - "reference" + - "api" + - "v2" +version: "2.0" +description: "Whether the code running now is claiming server-rendered DOM: the root pass of `hydrate()`, or code under a streamed `` boundary while that boundary resumes." +source_repo: "solidjs/solid" +source_ref: "next" +source_path: "packages/solid/src/client/hydration.ts" +--- + +{/* Generated by scripts/extract-solid-ref.mjs. Edit the source JSDoc or disposition map, then regenerate. */} + +Whether the code running now is claiming server-rendered DOM: the root +pass of `hydrate()`, or code under a streamed `` boundary while +that boundary resumes. `false` on the server, in client-only renders, +after hydration, and in a render a resume window triggers outside the +resuming boundary (that render builds fresh DOM). + +Not reactive: read it where a component or primitive is created. While it +is `true`, render what the server rendered, and switch to the client-only +value from `onSettled`. + +## Import + +```ts +import { isHydrating } from "solid-js"; +``` + +## Type signature + +```ts +function isHydrating(): boolean; +``` + +## Examples + +```ts +const [width, setWidth] = createSignal(isHydrating() ? 0 : el.offsetWidth); +onSettled(() => { + setWidth(el.offsetWidth); +}); +``` + +## Learn more + +- [Controlling hydration](/concepts/rendering-and-ssr#controlling-hydration) diff --git a/src/routes/reference/(1)solid-js/(6)advanced/(7)diagnostics-dev-hooks/observe.mdx b/src/routes/reference/(1)solid-js/(6)advanced/(7)diagnostics-dev-hooks/observe.mdx index c45cec92b..df5d9d435 100644 --- a/src/routes/reference/(1)solid-js/(6)advanced/(7)diagnostics-dev-hooks/observe.mdx +++ b/src/routes/reference/(1)solid-js/(6)advanced/(7)diagnostics-dev-hooks/observe.mdx @@ -231,6 +231,7 @@ handler's `intercept`, at t = 0) made no request and emits nothing. ```ts interface CallEvent { id: string; + name?: string; at: number; durationMs: number; method: "GET" | "POST"; @@ -247,6 +248,17 @@ interface CallEvent { The function id — the same `id` the server's `"invocation"` record carries. +#### `name` + +- **Type:** `string` + +The function's source name, from the reference's metadata +(`ServerFunctionMetadata.name`): the compiled function's name, which +the compiler seeds in development builds only, or an explicit label +(`withMeta`, the `name` argument to `createServerReference`), which +survives to production. A label, not an identity: not unique, and +absent when the metadata carries none. + #### `at` - **Type:** `number` @@ -310,15 +322,33 @@ type CallListener = (event: CallEvent, live: CallLive) => void; ### `CallLive` -The live half of a call, for in-process consumers. `response` is the -transport's own object, not a clone: its status and headers are -readable, its body is the decode's (already consumed, or being consumed -by the caller for a streaming result). `error` is the value as thrown to -the caller — a decoded server error, or the transport's own failure. +The live half of a call, for in-process consumers — ONE object across +the call's records: the same `CallLive` is handed to the `"request"` +listener at the send and to the `"call"` listener at settle, filled in +as the call proceeds (`args` from the start, `request` at the send, +`response` and `result`/`error` at settle — filled on every settle, +whether or not a `"call"` record goes out, so a `"request"` listener +holding it reads the outcome off it too), so an in-process consumer +joins a call's request to its settle by identity — the way `origin` +joins a record to the interaction's — with no id-plus-time join and no +sequence number. One MUTABLE object shared by both records' listeners: +treat it as read-only — a write shows up on the other record's +listener. `error` is the value as thrown to the caller — a +decoded server error, or the transport's own failure. The bodies — +`request`, and `response` as an unread clone — are a body viewer's +(devtools' network panel), and are taken only while a listener asked for +them (`OBSERVE.records.subscribe("call", fn, { bodies: true })`, or the +same on `"request"` for the request alone): each costs the call a +reconstruction and a transient double-buffer of the payload, which a +consumer that reads ids, statuses and timings never pays. With no such +listener `request` is absent and `response` is the transport's own +object, consumed by its decode. Whether bodies are taken is read once, +as the call starts. ```ts interface CallLive { args: unknown[]; + request?: Request; response?: Response; result?: unknown; error?: unknown; @@ -329,10 +359,58 @@ interface CallLive { - **Type:** `unknown[]` +#### `request` + +- **Type:** `Request` + +Bodies opted in — by a `"call"` listener or a `"request"` listener +(`observed("call", "bodies") || observed("request", "bodies")`): the +request as dispatched — the final url and `RequestInit` (the +transport's headers, the `prepareRequest` hook applied), built into a +`Request` of the listener's own at the send, so its headers and body +are readable in full and reading them touches nothing the transport +sent. Set before the `"request"` record is delivered, so that listener +reads it too — and it is ONE `Request`, shared by the call's +`"request"` and `"call"` records: a body reads once, so a listener that +wants it reads through `request.clone()` (`live.request.clone().text()`) +and the other record's listener can still read it; a direct +`live.request.text()` leaves `bodyUsed` true for whoever reads next. +Built WITH the body only when the body has a shape a +second `Request` can hold without a competing consumer — `string`, +`URLSearchParams`, `FormData`, `Blob`, `ArrayBuffer` or a view of one +— and WITHOUT it otherwise (a `ReadableStream` or an async iterable, +the transport's streaming-upload contract: reconstructing one would +consume it ahead of the send), so the request then reads as bodyless. +Absent without the opt-in; when the call failed before the request was +built (argument serialization threw); when the address is relative and +there is no `location` to resolve it against (absent beats a URL that +was never sent); and when the reconstruction itself failed (an init +the `Request` constructor rejects but the configured `fetch` +tolerates) — a reconstruction never fails the call. + #### `response` - **Type:** `Response` +The response: with bodies opted in, one with an UNREAD body — a +`clone()` taken as the response arrived, before the transport's +decode, so the listener reads status, headers and body while the +caller still gets its result from the original. The transport's own +object instead — status and headers readable, body consumed by the +transport's decode — without the opt-in, and, with it, for a response +whose clone would be a branch nobody drains: an event-stream response +(a `live()` source, `text/event-stream`, a connection open for the +page's life), a deferred result (a generator's stream, `deferred: +true`, served for the stream's life — its clone, taken before the +result's shape was known, is cancelled at settle), and a response the +`clone()` refused (one a configured `fetch` handed over already read). +A response a `responseHandler` claimed whose result is not a deferred +body — a non-live `application/x-frame-stream` frame render the frames +transport claims, say — keeps its clone, so under the opt-in the +clone's branch buffers that render until the listener reads it or +drops the record; live frames are `text/event-stream` and are never +cloned. Absent when the fetch itself rejected. + #### `result` - **Type:** `unknown` @@ -345,6 +423,96 @@ The settled value, when `outcome` is `"ok"`. The thrown value, when `outcome` is `"error"`. +### `CallRequestEvent` + +One server-function request LEFT the browser — delivered on +`OBSERVE.records.subscribe("request", …)` at the send: after the +arguments were serialized and `prepareRequest` had its say, immediately +before the transport's `fetch` is handed the request. "Left" means +HANDED TO `fetch`, not that it reached the network: a `fetch` that +throws synchronously still has its `"request"` (and a `"call"` with +`outcome: "error"`). The `"call"` record of the same call follows at +settle; this one exists because that one cannot show a call that is +still in flight, or one that never settles (a hung fetch) — a network +panel's pending row. The two records share their `CallLive` by identity +(see `CallLive`): the object handed here is the object handed to the +`"call"` listener, so an in-process consumer joins them with no +id-plus-time join and no sequence number. + +Emitted only for a request that was handed over: a call that failed +before its request was built (argument serialization threw, or +`prepareRequest` did) emits no `"request"` — only its `"call"` settle; a +call an integration answered locally (a handler's `intercept`) made no +request and emits neither. A deferred or streaming result emits +`"request"` at the send and `"call"` at handoff, as today. Serializable; +`live.request` (under `bodies`) rides beside it. + +Listeners run synchronously, on the call's path, BEFORE the send: a slow +listener delays the fetch and the time it spends is inside the call's +`durationMs`. Read what is needed and hand the work off to a microtask +(the same guidance the engine's records give). + +```ts +interface CallRequestEvent { + side: "client"; + id: string; + name?: string; + at: number; + method: "GET" | "POST"; + origin?: ChangeOrigin; +} +``` + +#### `side` + +- **Type:** `"client"` + +Which end recorded it. Only `"client"` exists today — the request left +the browser. The server half (the request arrived, ahead of its +`"invocation"`) is `"server"`, additive later, the way the `"frame"` +record has two halves. + +#### `id` + +- **Type:** `string` + +The function id — the same `id` the call's `"call"` and the server's `"invocation"` carry. + +#### `name` + +- **Type:** `string` + +The function's source name, by the same rule as `CallEvent.name`. + +#### `at` + +- **Type:** `number` + +`performance.now()` at the send — when the request was handed to +`fetch`, after serialization and `prepareRequest`. NOT the call's +`CallEvent.at`, which is when the call was made: the gap between them +is what building the request cost (an async `prepareRequest` +included), and `CallEvent.at + durationMs` is never before this. + +#### `method` + +- **Type:** `"GET" | "POST"` + +`GET` for a GET-encoded read (`GET(fn)`), `POST` otherwise — as on `CallEvent`. + +#### `origin` + +- **Type:** `ChangeOrigin` + +What the call ran for — the same object `CallEvent.origin` carries, +read at dispatch (see `CallEvent.origin` for the rule). + +### `CallRequestListener` + +```ts +type CallRequestListener = (event: CallRequestEvent, live: CallLive) => void; +``` + ### `DiagnosticCapture` ```ts @@ -955,9 +1123,11 @@ prod with the rest of `OBSERVE`. interface Records { subscribe( type: K, - listener: RecordListener + listener: RecordListener, + options?: RecordSubscribeOptions ): () => void; observed(type: RecordType): boolean; + observed(type: RecordType, facet: "bodies"): boolean; emit( type: K, event: RecordEvent, @@ -973,7 +1143,13 @@ interface Records { Deliver `type` records as they complete; returns the unsubscribe. The subscription is the channel's, not any emitter's: it outlives the attribution engine's `enable()`/`disable()` cycles and is dropped only -by its own unsubscribe. +by its own unsubscribe. `options` asks the emitter for more than the +record — see `RecordSubscribeOptions`. One entry per listener function: +its options are read at its first subscription to the type, a repeat +subscription of the same function changes nothing, and either disposer +removes it. What an emitter takes is decided once per record from the +union of the type's subscribers, so a listener without `bodies` that +shares a call with one that asked receives the same `live` — the clone. #### `observed` @@ -982,6 +1158,16 @@ by its own unsubscribe. Whether anything is subscribed to `type` — an emitter's pre-check, so a record nobody will hear costs nothing to not build (no clock read). +#### `observed` + +- **Type:** `boolean` + +Whether a listener of `type` asked for its bodies +(`subscribe(type, listener, { bodies: true })`) — the emitter's +pre-check for the live handles that cost something per record to take. +`false` while every listener of the type is a plain one, and once the +last body-wanting one unsubscribed. + #### `emit` - **Type:** `void` @@ -992,6 +1178,31 @@ list is replaced, never mutated, on subscribe/unsubscribe — so a listener unsubscribing mid-delivery neither skips nor double-calls anyone this round, and delivery allocates nothing. +### `RecordSubscribeOptions` + +What a `Records.subscribe` asks of the emitter beyond the record itself. + +```ts +interface RecordSubscribeOptions { + bodies?: boolean; +} +``` + +#### `bodies` + +- **Type:** `boolean` + +Ask for the record's BODIES: the live handles that cost the emitter +something per record to take, and are taken only while a listener of +the type has asked — so a consumer that reads ids, statuses and +timings (an APM adapter, the performance tracks) never pays for what a +body viewer (devtools' network panel) reads. Accepted for any type, the +channel being generic; meaningful today for `"call"`, whose +`live.request` (a reconstruction of the dispatched request) and +`live.response` (an unread clone, a transient double-buffer of the +payload) are taken only under it — without it, the transport's own +objects. An emitter asks with `observed(type, "bodies")`. + ### `RecordType` ```ts @@ -1173,6 +1384,7 @@ interface RenderEvent { durationMs: number; boundaries: number; outcome: "complete" | "abandoned" | "error"; + route?: RenderRoute; } ``` @@ -1229,6 +1441,21 @@ left mid-stream (the sink threw, the readable was cancelled — the render was torn down; `"error"` — the render failed: a string render threw, a stream's uncontained failure wound it down through `onError`. +#### `route` + +- **Type:** `RenderRoute` + +The route the render resolved to, as the router declared it while +building its context under this render (`OBSERVE.attribution.withOrigin` +with an `initial` ref — the same declaration the client's first +`"navigation"` record comes from): `name` the matched pattern +(`/users/:id`), `to` the concrete path, `params` what the pattern +bound. Read from the router's ref when the render settles, so a match +refined during the render is what lands. Absent when no router declared +one — a render without a router, or a router that does not yet. What a +consumer names the request by (`http.route`), where the URL would +scatter one page across as many names as it has parameters. + ### `RenderListener` ```ts @@ -1258,6 +1485,36 @@ The request event the render ran under; absent for a render outside a request sc The trace the render belongs to — `getTraceContext()`'s answer for it. +### `RenderRoute` + +`RenderEvent.route` — the route a render resolved to, as the router matched it. + +```ts +interface RenderRoute { + name?: string; + to?: string; + params?: Readonly>; +} +``` + +#### `name` + +- **Type:** `string` + +The matched route pattern — `/users/:id`. + +#### `to` + +- **Type:** `string` + +The concrete path. + +#### `params` + +- **Type:** `Readonly>` + +The params the pattern bound (optional params unbound: `undefined`). + ### `ServerObserve` The server runtime's observe surface — where a server-side consumer diff --git a/src/routes/reference/(2)solid-web/(1)rendering-ssr/get-hydration-writer.mdx b/src/routes/reference/(2)solid-web/(1)rendering-ssr/get-hydration-writer.mdx new file mode 100644 index 000000000..5549af308 --- /dev/null +++ b/src/routes/reference/(2)solid-web/(1)rendering-ssr/get-hydration-writer.mdx @@ -0,0 +1,92 @@ +--- +title: "getHydrationWriter" +category: "Rendering & SSR" +use_cases: "rendering & ssr api, gethydrationwriter usage" +tags: + - "get" + - "hydration" + - "writer" + - "rendering" + - "ssr" + - "reference" + - "api" + - "v2" +version: "2.0" +description: "The keyed server-to-client value channel of the render the caller belongs to — found through the caller's owner, else (no owner) through the request scope when exactly one render is open for that request." +source_repo: "solidjs/solid" +source_ref: "next" +source_path: "packages/web/src/server.ts" +--- + +{/* Generated by scripts/extract-solid-ref.mjs. Edit the source JSDoc or disposition map, then regenerate. */} + +The keyed server-to-client value channel of the render the caller belongs +to — found through the caller's owner, else (no owner) through the request +scope when exactly one render is open for that request. `undefined` +outside any render and on the client. Never another request's render. + +Capture it where the render is known (component setup) to write from IO +later. A write is not gated on ``: read `isHydratable()` where +the value is produced to decide. Read the value on the client with +`takeHydrationValue(key)`. + +## Import + +```ts +import { getHydrationWriter } from "@solidjs/web"; +``` + +## Type signature + +```ts +function getHydrationWriter(): HydrationWriter | undefined; +``` + +## Examples + +```ts +const writer = getHydrationWriter(); +if (writer && isHydratable()) writer.write("sq:" + hash, data); +``` + +## Learn more + +- [Rendering and SSR](/concepts/rendering-and-ssr) +- [Choose a rendering mode](/guides/choose-a-rendering-mode) + +## Related types + +### `HydrationWriter` + +A render's keyed server-to-client value channel (`getHydrationWriter`). + +```ts +interface HydrationWriter { + readonly async: boolean; + write( + key: string, + value: unknown, + options?: { deferStream?: boolean } + ): boolean; +} +``` + +#### `async` + +- **Type:** `boolean` + +Whether the render takes promises and async iterables (`renderToStream`). +`renderToString` takes synchronous values only; `write` throws on an +async one. + +#### `write` + +- **Type:** `boolean` + +Serializes `value` for the client under `key`, readable there with +`takeHydrationValue(key)`. Returns `false` without writing when `key` was +already written by this render (the first write wins) or the render no +longer takes values (a finished `renderToString`, a completed stream). +`deferStream` holds the shell until a promise settles. Prefix keys with +the library's own namespace (`"sq:"`); Solid's own entries are keyed by +hydration id. diff --git a/src/routes/reference/(2)solid-web/(1)rendering-ssr/take-hydration-value.mdx b/src/routes/reference/(2)solid-web/(1)rendering-ssr/take-hydration-value.mdx new file mode 100644 index 000000000..1908cc86b --- /dev/null +++ b/src/routes/reference/(2)solid-web/(1)rendering-ssr/take-hydration-value.mdx @@ -0,0 +1,69 @@ +--- +title: "takeHydrationValue" +category: "Rendering & SSR" +use_cases: "rendering & ssr api, takehydrationvalue usage" +tags: + - "take" + - "hydration" + - "value" + - "rendering" + - "ssr" + - "reference" + - "api" + - "v2" +version: "2.0" +description: "Removes and returns the value the server render wrote under `key` with `getHydrationWriter().write(key, value)`, or `undefined` when the page carries none (or it was already taken)." +source_repo: "solidjs/solid" +source_ref: "next" +source_path: "packages/web/src/client.ts" +--- + +{/* Generated by scripts/extract-solid-ref.mjs. Edit the source JSDoc or disposition map, then regenerate. */} + +Removes and returns the value the server render wrote under `key` with +`getHydrationWriter().write(key, value)`, or `undefined` when the page +carries none (or it was already taken). A written promise arrives as +`"pending"` until its settlement streams in, then as `"resolved"` or +`"rejected"`. Readable before `hydrate()` runs and after hydration has +ended. Server: `undefined`. + +The page's values live on `globalThis._$HY.r`; a test seeds them with +`globalThis._$HY = { r: { "sq:key": value } }`. + +## Import + +```ts +import { takeHydrationValue } from "@solidjs/web"; +``` + +## Type signature + +```ts +function takeHydrationValue( + key: string +): HydrationValue | undefined; +``` + +## Parameters + +### `key` + +- **Type:** `string` + +## Learn more + +- [Rendering and SSR](/concepts/rendering-and-ssr) +- [Choose a rendering mode](/guides/choose-a-rendering-mode) + +## Related types + +### `HydrationValue` + +A keyed value taken from the hydration registry (`takeHydrationValue`). + +```ts +type HydrationValue = + | { status: "resolved"; value: T } + | { status: "rejected"; error: unknown } + | { status: "pending"; promise: Promise }; +```