-
-
Notifications
You must be signed in to change notification settings - Fork 0
useDeferred
-
Name:
useDeferred<T>(key: string) -
Purpose: Read a deferred Promise published by an SSR loader via
defer({ deferred: { <key>: Promise } }). Returns the rawPromise<T>for use inside{#await}blocks or the higher-level<Await>/<Streamed>components. - When to use: Inside a route component that consumes deferred (non-critical) SSR data.
-
Availability: All 5 framework adapters expose a
useDeferredcomposable/hook with symmetric semantics —@real-router/react/ssr,@real-router/preact/ssr,@real-router/solid/ssr,@real-router/vue/ssr,@real-router/svelte/ssr.
<script lang="ts">
import { useDeferred } from "@real-router/svelte/ssr";
import type { Review } from "./types";
const reviewsPromise = useDeferred<Review[]>("reviews");
</script>
{#await reviewsPromise}
<Spinner />
{:then reviews}
<ReviewList items={reviews} />
{:catch error}
<ErrorBanner message={error.message} />
{/await}function useDeferred<T = unknown>(key: string): Promise<T>;| Parameter | Type | Description |
|---|---|---|
key |
string |
Identifier matching a key in the loader's defer({ deferred: { <key>: Promise } }). |
Returns: Promise<T> — the deferred Promise from state.context.ssrDataDeferred[key]. If the key is missing, returns a forever-pending Promise.
export function useDeferred<T = unknown>(key: string): Promise<T> {
const { route } = useRoute();
const context = route.current.context as DeferredContext;
const deferred = context.ssrDataDeferred;
return (deferred?.[key] ?? NEVER_PROMISE) as Promise<T>;
}- Reads
state.context.ssrDataDeferredfrom the current route, populated by@real-router/ssr-data-plugin'sdefer()helper. - Calls
useRoute()— throws if no active state (unstarted/stopped/disposed) or outside a<RouterProvider>. - Returns the Promise verbatim, or a forever-pending
NEVER_PROMISEsingleton if the key is missing.
If key doesn't match a loader-declared entry, useDeferred returns a shared NEVER_PROMISE that never resolves. This surfaces loader/consumer key drift as a visible loading state ({#await} stays in the pending branch) rather than a silent runtime error.
<!-- Loader declares "reviews", consumer asks for "reveiws" (typo) -->
{#await useDeferred("reveiws")}
<Spinner /> <!-- Forever-pending — never resolves. -->
{:then reviews}
…
{/await}During development, a permanent spinner is an easy signal that the key is misspelled.
The <T> generic lets you type the resolved value:
interface Review {
id: string;
text: string;
}
const reviewsPromise = useDeferred<Review[]>("reviews");
// reviewsPromise: Promise<Review[]>The cast is one-way — the helper doesn't validate the runtime shape. Wrong key + wrong type → forever-pending (the runtime never resolves to validate).
Like all Svelte composables in this adapter, useDeferred calls getContext (via useRoute) which requires synchronous component init. Don't call it inside $effect or async callbacks — see Svelte Integration Guide.
useDeferred(key) returns a plain Promise. If key is a $state rune, wrap the call in $derived to re-resolve on key change:
<script lang="ts">
let selectedTab = $state("reviews");
const promise = $derived(useDeferred(selectedTab));
</script>
{#await promise}
<Spinner />
{:then data}
<TabContent {data} />
{/await}<Await name={selectedTab}> does this internally — see Await.
<script lang="ts">
import { useDeferred } from "@real-router/svelte/ssr";
const reviews = useDeferred<Review[]>("reviews");
</script>
{#await reviews}
<Spinner />
{:then list}
<ReviewList items={list} />
{:catch error}
<ErrorBanner message={error.message} />
{/await}<script lang="ts">
import { Await } from "@real-router/svelte/ssr";
</script>
<Await<Review[]> name="reviews">
{#snippet children(reviews)}
<ReviewList items={reviews} />
{/snippet}
{#snippet fallback()}
<Spinner />
{/snippet}
</Await><Await> builds on useDeferred — see Await for when to prefer the higher-level component.
// routes/product.loader.ts
import { defer } from "@real-router/ssr-data-plugin";
export const loader = async (params) => {
return defer({
// Critical — awaited inline before render starts.
product: await fetchProduct(params.id),
// Deferred — runs in parallel, surfaces via state.context.ssrDataDeferred.
deferred: {
reviews: fetchReviews(params.id),
similar: fetchSimilarProducts(params.id),
},
});
};<!-- routes/Product.svelte -->
<script lang="ts">
import { useRoute } from "@real-router/svelte";
import { useDeferred } from "@real-router/svelte/ssr";
const { route } = useRoute();
const product = route.current.context.data?.product;
const reviews = useDeferred("reviews");
const similar = useDeferred("similar");
</script>
{#if product}
<ProductHero {product} />
{#await reviews}
<Spinner label="Loading reviews" />
{:then list}
<ReviewList items={list} />
{/await}
{#await similar}
<Spinner label="Loading recommendations" />
{:then list}
<SimilarProducts items={list} />
{/await}
{/if}<script lang="ts">
import { useDeferred } from "@real-router/svelte/ssr";
const reviews = useDeferred<Review[]>("reviews");
const ratings = useDeferred<Rating[]>("ratings");
// Race or all-of:
const allDataPromise = Promise.all([reviews, ratings]);
</script>
{#await allDataPromise}
<Spinner />
{:then [reviewList, ratingList]}
<ReviewSection reviews={reviewList} ratings={ratingList} />
{/await}- Missing key → forever-pending. Verify loader and consumer agree on key names. The visible spinner is the diagnostic signal.
-
Throws outside
<RouterProvider>or with no active route.useDeferredcallsuseRoute()which throws in either case. Call only inside route components or afterrouter.start()has resolved. -
Must be called during component init. Don't call inside
$effect, event handlers, or async callbacks —getContextfails outside init. -
No runtime type validation. The
<T>generic is a cast, not a schema. Trust the loader to publish the right shape, or validate at the await site. -
Server-only Promise resolution by default. The
deferredPromises are created on the server inside the loader. On the client side (after hydration), the same Promises continue resolving — Svelte 5 stable rehydrates the in-flight Promises rather than re-fetching. Verify withexamples/web/svelte/ssr-examples/ssr-streaming/.
- Requires
<RouterProvider>mounted withssr-data-plugin(or a custom plugin that populatesstate.context.ssrDataDeferred). - Loader must declare
defer({ deferred: { <key>: Promise } }).
-
Await — higher-level component that wraps
useDeferred+{#await}withnameprop - Streamed — Suspense-style boundary for grouping multiple deferred reads
- Streaming SSR — cross-framework streaming model
- Svelte Integration Guide — full SSR surface overview
-
@real-router/ssr-data-plugin— thedefer()server-side primitive
- View-Agnostic Design
- Core Concepts
- Navigation Lifecycle
- Guards
- Plugin Architecture
- Hash Fragment Support
- Accessibility (A11y)
- Server-Side Rendering
- Data Loading
- Streaming SSR
- SSR Cancellation
- RSC Integration
- Testing
- Glossary
Tree-shakeable functions — import only what you need.
- add (addRoute)
- remove (removeRoute)
- replace (replaceRoutes)
- clear (clearRoutes)
- get (getRoute)
- has (hasRoute)
- update (updateRoute)
- get (getDependency)
- getAll (getDependencies)
- set (setDependency)
- setAll (setDependencies)
- has (hasDependency)
- remove (removeDependency)
- reset (resetDependencies)
For plugin authors, not for general use.
- makeState
- buildState
- buildNavigationState
- forwardState
- getForwardState
- setForwardState
- matchPath
- setRootPath
- getRootPath
- navigateToState
- addEventListener
- getRouteConfig
- getOptions
- addBuildPathInterceptor
- extendRouter
- getTree
- React Integration Guide
- Preact Integration Guide
- Solid Integration Guide
- Vue Integration Guide
- Svelte Integration Guide
- Ink (Terminal UI) Integration Guide
- Desktop (Electron, Tauri) Integration Guide
- useRouter
- useRoute
- useRouteNode
- useNavigator
- useRouteUtils
- useRouterTransition
- useRouteExit
- useRouteEnter
- useRouteStore (Solid only)
- useRouteNodeStore (Solid only)
SSR-feature subpath —
@real-router/{adapter}/ssr. Symmetric across React/Preact/Solid/Vue/Svelte.
- Lazy (Svelte only — dynamic component import)
- Await — read deferred SSR data by key
- Streamed — Suspense-style boundary
- ClientOnly — server fallback → client children swap
- ServerOnly — server children, removed after hydration
- HttpStatusCode — render-time HTTP status declaration
-
HttpStatusProvider — provides sink to descendant
<HttpStatusCode> - useDeferred — read deferred Promise by key