30% offEnding soon
All questions

createSuspenseResource

Premium

createSuspenseResource

A Suspense resource turns asynchronous work into a synchronous-looking read: it returns cached data or throws the cache entry's promise or error. Build a keyed resource that deduplicates requests and a SuspenseData render wrapper that reads during render. React catches a thrown pending promise at the nearest <Suspense> boundary, shows its fallback, and retries after settlement.

Signature

function createSuspenseResource(loader: (key: unknown) => unknown): {
  read(key: unknown): unknown;
  preload(key: unknown): Promise<unknown>;
  clear(key?: unknown): void;
};

function SuspenseData({
  resource,
  resourceKey,
  children: (data: unknown) => React.ReactNode,
}): React.ReactNode;

Examples

const users = createSuspenseResource((id) => fetchUser(id));

users.read(42);
// First call starts one request and throws its promise.
// After fulfillment, read(42) returns the exact cached user.
<Suspense fallback={<p>Loading…</p>}>
  <SuspenseData resource={users} resourceKey={42}>
    {(user) => <Profile user={user} />}
  </SuspenseData>
</Suspense>

Notes

  • Cache by identity — use a Map, so primitives follow SameValueZero comparison and object keys match only the same reference.
  • Preserve exact values — pending reads throw one exact promise; fulfilled reads return the exact value; rejected reads throw the exact error. Cache both outcomes.
  • Preload consistentlypreload(key) starts or deduplicates work and always returns that entry's original promise, even after settlement.
  • Distinguish clear callsclear() clears all entries, while clear(undefined) deletes only the explicit undefined key.
  • Read during renderSuspenseData validates resource.read and its function child, then calls children(resource.read(resourceKey)) only when ready.
  • Keep the scope narrow — do not add retries, TTLs, stale data, cancellation, mutation, a global cache, an error boundary, or a fetching library.